Como um script Bash pode dizer como foi executado?

10

Eu tenho um script Bash que estava tentando fazer para me ajudar a executar um comando bastante complexo com pequenas alterações que ele me perguntaria através do eco e da leitura.

Encontrei soluções para forçá-lo a executar um terminal para executar o comando, mas não estou interessado nisso. O que eu gostaria de fazer é que, se eu espaçar e apenas pressionar Enter nele no Nautilus (executando-o com o Run Software), uma gentilmente abrirá uma notificação dizendo "Por favor, execute isso a partir de um terminal".

Posso fazer com que o pop-up aconteça - como eu conheço o comando - mas não consigo que o script Bash diga se está sendo executado dentro de um terminal ou não, parece que sempre penso assim. Isso é possível?

Aescula
fonte

Respostas:

10

De man bashsob EXPRESSÕES CONDICIONAIS :

-t fd  
    True if file descriptor fd is open and refers to a terminal.

Supondo que o fd 1 seja padrão, if [ -t 1 ]; thendeve funcionar para você. O Advanced Shell Scripting Guide alega que o -tuso dessa maneira falhará sshe que o teste (usando stdin, não stdout) deve, portanto, ser:

if [[ -t 0 || -p /dev/stdin ]]

-ptesta se um arquivo existe e é um pipe nomeado. No entanto , notei que, experimentalmente, isso não é verdade para mim: -p /dev/stdinfalha nos terminais normais e nas sessões ssh, enquanto if [ -t 0 ](ou -t 1) funciona nos dois casos (consulte também os comentários de Gilles abaixo sobre os problemas nessa seção do Guia Avançado de Script de Shell ).


Se o problema principal for um contexto especializado a partir do qual você deseja chamar o script para se comportar de maneira apropriada a esse contexto, você pode contornar todos esses detalhes técnicos e poupar um pouco de trabalho usando um wrapper e uma variável personalizada:

!#/bin/bash

export SPECIAL_CONTEXT=1
/path/to/real/script.sh

Ligue para isso live_script.shou o que for e clique duas vezes nele. Obviamente, você poderia realizar a mesma coisa com argumentos de linha de comando, mas ainda seria necessário um wrapper para fazer apontar e clicar em um navegador de arquivos da GUI.

Cachinhos Dourados
fonte
5
esta é a resposta correta - é também como o POSIX diz que um shell deve detectar se é interativo ou não.
mikeserv
2
@DanielAmaya - se você redirecionar a entrada, o script não está sendo executado em um terminal. A questão é como detectar se o script está sendo executado em um terminal.
mikeserv
2
Você tem certeza do uso do ||interior [ … ]assim? Se você usar [[ … ]], tudo ficará bem, mas normalmente o ||é usado para separar comandos e [ -t 0é uma invocação incorreta [porque ]está faltando o último . Normalmente, também não há um comando -p. Eu concordo com o teste para um terminal; essa é provavelmente a maneira de fazer isso. É apenas a sintaxe que me preocupa.
Jonathan Leffler
1
@JonathanLeffler Right; isso deve produzir um erro de sintaxe, pois o operador shell ||é visto antes do ]argumento final necessário para [.
chepner
3
Essa seção do Advanced Bash-Scripting Guide apresenta vários erros. PS1Não é um teste confiável para saber se o shell é interativo. “Se um script precisa testar se está sendo executado em um shell interativo” também é confuso: deve ser se algum código precisar ser testado - um script geralmente não está sendo executado em um shell interativo (mas pode ser, se for de origem) . Testar para iin $-é a maneira correta de testar se o shell é interativo. Testar -t 0ou -t 2é a maneira correta de saber se o script está sendo executado em um terminal, diferente de ser interativo.
Gilles 'SO- stop be evil'
0

Use a variável bash $ SHLVL para detectar o nível de aninhamento de shell. Em um script, execute 'raw' clicando duas vezes em 1; em um script em execução em um terminal, será 2.

#!/bin/bash
if (( SHLVL < 2 )) ; then
    echo "Please run this from a terminal."
    read -p "Press <Enter> to close this window"
    exit 1
fi
# rest of script
Ed Randall
fonte
0

Embora a resposta dos goldilocks esteja provavelmente correta no caso típico, parece que existem casos extremos. No meu próprio caso, meu xserver está configurado para iniciar tty1e nunca sai desse tty. Se o Xorg stdouté um TTY, parece que os clientes terão esse TTY vinculado ao seu descritor de arquivo por padrão.

Veja como eu resolvi meu problema:

#!/bin/bash
isxclient=$( readlink /dev/fd/2 | grep -q 'tty' && [[ -n $DISPLAY ]] ; echo $? )
if [[ ! -t 2  || $isxclient == "0" ]]; then
        notify-send "Script wasn't started from an interactive shell"
else
        echo "Script was started from an interactive shell"
fi

Não testei isso para ver se funciona em uma configuração X mais padrão, e também duvido muito que esse seja o único caso extremo. Se alguém encontrar uma solução mais aplicável, volte e informe-nos.

JMW
fonte
-2

Outro, usando as opções do bash, define a variável interna $-,.

De .bashrc,

# If not running interactively, don't do anything
case $- in
    *i*) ;;
    *) return;;
esac
xae
fonte
um shell interativo não está necessariamente conectado a um terminal. enquanto um começou com a conexão é automaticamente iniciado interativo, isso também é possível: cmd | sh -i | cmd.
mikeserv
Este código está sendo executado em um script. Não será interativo, mesmo que esteja sendo executado em um terminal.
Gilles 'SO- stop be evil'