Para que serve a variável $ BASH_COMMAND?

23

De acordo com o manual de Bash , a variável de ambiente BASH_COMMAND contém

  

O comando atualmente sendo executado ou prestes a ser executado, a menos que o shell esteja executando um comando como resultado de um trap, neste caso, é o comando que está sendo executado no momento do trap.

Excluindo esse caso de canto de trap, se eu entendi corretamente, isso significa que, quando eu executo um comando, a variável BASH_COMMAND contém esse comando. Não está absolutamente claro se essa variável não está definida após a execução do comando (isto é, é apenas avaialble enquanto o comando está em execução, mas não depois), embora se possa argumentar que ele é "o comando atualmente sendo executado ou prestes a ser executado ", não é o comando que foi executado .

Mas vamos verificar:

$ set | grep BASH_COMMAND=
$ 

Vazio. Eu teria esperado ver BASH_COMMAND='set | grep BASH_COMMAND=' ou talvez apenas BASH_COMMAND='set' , mas vazio me surpreendeu.

Vamos tentar outra coisa:

$ echo $BASH_COMMAND
echo $BASH_COMMAND
$ 

Bem, isso faz sentido. Eu executo o comando echo $BASH_COMMAND e assim a variável BASH_COMMAND contém a string echo $BASH_COMMAND . Por que isso funcionou desta vez, mas não antes?

Vamos fazer a coisa set novamente:

$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$

Então espere. Foi definido quando executei o comando echo e não foi cancelado depois. Mas quando eu executei set novamente, BASH_COMMAND não foi definido para o comando set . Não importa quantas vezes eu execute o comando set aqui, o resultado permanece o mesmo. Então, a variável é definida ao executar echo , mas não ao executar set ? Vamos ver.

$ echo Hello AskUbuntu
Hello AskUbuntu
$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$

O que? Então a variável foi definida quando executei echo $BASH_COMMAND , mas não quando executei echo Hello AskUbuntu ? Onde está a diferença agora? A variável é definida apenas quando o próprio comando atual força o shell a avaliar a variável? Vamos tentar algo diferente. Talvez algum comando externo desta vez, não um estrondo, por uma mudança.

$ /bin/echo $BASH_COMMAND
/bin/echo $BASH_COMMAND
$ set | grep BASH_COMMAND=
BASH_COMMAND='/bin/echo $BASH_COMMAND'
$

Hmm, ok ... novamente, a variável foi definida. Então, meu palpite atual está correto? A variável é definida apenas quando precisa ser avaliada? Por quê? Por quê? Por motivos de desempenho? Vamos fazer mais uma tentativa. Vamos tentar grep para $BASH_COMMAND em um arquivo, e como $BASH_COMMAND deve conter um comando grep , grep deve grep para o comando grep (ou seja, para ele mesmo). então vamos fazer um arquivo apropriado:

$ echo -e "1 foo\n2 grep\n3 bar\n4 grep $BASH_COMMAND tmp" > tmp
$ grep $BASH_COMMAND tmp
grep: $BASH_COMMAND: No such file or directory
tmp:2 grep                                      <-- here, the word "grep" is RED
tmp:4 grep $BASH_COMMAND tmp                    <-- here, the word "grep" is RED
tmp:2 grep                                      <-- here, the word "grep" is RED
tmp:4 grep $BASH_COMMAND tmp                    <-- here, the word "grep" is RED
$ set | grep BASH_COMMAND=
BASH_COMMAND='grep --color=auto $BASH_COMMAND tmp'
$

Ok, interessante. O comando grep $BASH_COMMAND tmp foi expandido para grep grep $BASH_COMMAND tmp tmp (a variável é expandida apenas uma vez, é claro) e, por isso, usei grep , uma vez em um arquivo $BASH_COMMAND que não existe e duas vezes no arquivo tmp .

Q1: Meu pressuposto atual é o seguinte:

  • BASH_COMMAND é definido apenas quando um comando tenta realmente avaliá-lo; e
  • não é não cancelado após a execução de um comando, mesmo que a descrição nos leve a acreditar nisso?

Q2: Se sim, por quê? Atuação? Se não, de que outra forma o comportamento na sequência de comandos acima pode ser explicado?

Q3: Por fim, existe algum cenário em que essa variável possa realmente ser usada de forma significativa? Eu estava realmente tentando usá-lo dentro de $PROMPT_COMMAND para analisar o comando que está sendo executado (e fazer algumas coisas dependendo disso), mas eu não posso, porque assim que, dentro do meu $PROMPT_COMMAND , eu executo um comando para procurar na variável $BASH_COMMAND , a variável obtém conjuntos para esse comando. Mesmo quando eu faço MYVARIABLE=$BASH_COMMAND no início do meu $PROMPT_COMMAND , então MYVARIABLE contém a string MYVARIABLE=$BASH_COMMAND , porque uma atribuição é um comando também. (Esta questão não é sobre como eu poderia obter o comando atual dentro de uma execução de $PROMPT_COMMAND . Há outras maneiras, eu sei.)

É um pouco como o princípio da incerteza de Heisenberg. Apenas observando a variável, eu mudo.

    
por Malte Skoruppa 20.08.2014 / 20:24

4 respostas

12

Respondendo à terceira questão: é claro que pode ser usado de maneira significativa na forma como o manual do Bash claramente sugere - em uma armadilha, e. g .:

$ trap 'echo ‘$BASH_COMMAND’ failed with error code $?' ERR
$ fgfdjsa
fgfdjsa: command not found
‘fgfdjsa’ failed with error code 127
$ cat /etc/fgfdjsa
cat: /etc/fgfdjsa: No such file or directory
‘cat /etc/fgfdjsa’ failed with error code 1
    
por Dmitry Alexandrov 21.08.2014 / 06:55
6

Agora que o Q3 foi respondido (corretamente, na minha opinião: BASH_COMMAND é útil em armadilhas e quase em qualquer outro lugar), vamos dar uma chance a Q1 e Q2.

A resposta para Q1 é: a exatidão de sua suposição é indecidível. A verdade de nenhum dos pontos pode ser estabelecida, como eles perguntam sobre o comportamento não especificado. Por sua especificação, o valor de BASH_COMMAND é definido como o texto de um comando pela duração da execução desse comando. A especificação não indica qual deve ser seu valor em qualquer outra situação, ou seja, quando nenhum comando está sendo executado. Pode ter algum valor ou nenhum.

A resposta para Q2 "Se não, de que outra forma o comportamento na sequência de comandos acima pode ser explicado?" em seguida, segue logicamente (se um pouco pedantemente): é explicado pelo fato de que o valor de BASH_COMMAND é indefinido. Como seu valor é indefinido, pode ter qualquer valor, que é exatamente o que a seqüência mostra.

Postscript

Há um ponto em que eu acho que você está realmente atingindo um ponto fraco na especificação. É onde você diz:

  

Mesmo quando faço MYVARIABLE = $ BASH_COMMAND no início do meu   $ PROMPT_COMMAND, então MYVARIABLE contém a string   MYVARIABLE = $ BASH_COMMAND, porque uma atribuição também é um comando .

A forma como eu leio a página man bash, a parte em itálico não é verdadeira. A seção SIMPLE COMMAND EXPANSION explica como primeiro as atribuições de variáveis na linha de comando são reservadas e, em seguida,

  

Se nenhum nome de comando resultar [em outras palavras, havia apenas variáveis   atribuições], as atribuições de variáveis afetam o shell atual   meio ambiente.

Isso sugere que atribuições de variáveis não são comandos (e, portanto, isentas de aparecer em BASH_COMMAND ), como em outras linguagens de programação. Isso também explicaria por que não há uma linha BASH_COMMAND=set na saída de set , set sendo essencialmente um 'marcador' sintático para atribuição de variável.

OTOH, no parágrafo final da seção, diz

  

Se houver um nome de comando deixado após a expansão, a execução prossegue como   Descrito abaixo. Caso contrário, o comando é encerrado.

... o que sugere o contrário, e as atribuições de variáveis também são comandos.

    
por zwets 21.08.2014 / 13:25
2

Um uso inventivo para $ BASH_COMMAND

Recentemente encontrei este uso impressionante de $ BASH_COMMAND na implementação de uma macro funcionalidade semelhante a.

  

Este é o truque principal do alias e substitui o uso da armadilha DEBUG. Se você ler a parte da postagem anterior sobre a interceptação DEBUG, você reconhecerá a variável $ BASH_COMMAND. Nesse post eu disse que ele foi definido para o texto do comando antes de cada chamada para a armadilha DEBUG. Bem, acontece que é definido antes da execução de cada comando, DEBUG trap ou não (por exemplo, execute "echo" this command = $ BASH_COMMAND "para ver do que estou falando). Atribuindo a ela uma variável (apenas para aquela linha), capturamos BASH_COMMAND no escopo mais externo do comando, que conterá o comando inteiro.

O artigo anterior do autor também fornece alguns bons antecedentes ao implementar uma técnica usando um DEBUG trap . O trap é eliminado na versão melhorada.

    
por DocSalvager 27.08.2014 / 08:53
0

Um uso aparentemente comum para a armadilha ... debug é melhorar os títulos que aparecem na lista de janelas (^ A ") quando você está usando a" tela ".

Eu estava tentando tornar a "tela", "windowlist" mais utilizável, então comecei a encontrar artigos referenciando "trap ... debug".

Descobri que o método que usa o PROMPT_COMMAND para enviar a "sequência de títulos nula" não funcionava tão bem, então reverti para o método de depuração ...

.

Veja como você faz isso:

1 Diga "screen" para procurar a seqüência de escape (habilite-a) colocando "shelltitle '$ | bash:'" em "$ HOME / .screenrc".

2 Desative a propagação da armadilha de depuração em sub-shells. Isto é importante, porque se estiver habilitado, tudo se estraga, use: "set + o functrace".

3 Envie a sequência de escape do título para "screen" para interpretar:     armadilha 'printf "\ ek $ (date +% Y% m% d% H% M% S) $ (whoami) @ $ (nome do host): $ (pwd) $ {BASH_COMMAND} \ e \"' "DEBUG"

Não é perfeito, mas ajuda, e você pode usar esse método para colocar literalmente o que quiser no título

Veja a seguinte "lista de janelas" (5 telas):

Sinalizadores de nome numérico

1 bash: 20161115232035 mcb @ ken007: /home/mcb/ken007 RSYNCCMD="sudo rsync" myrsync.sh -R -r "$ {1}" "$ {BACKUPDIR} $ {2: +" / $ { 2} "}" $    2 bash: 20161115230434 mcb @ ken007: / home / mcb / ken007 ls - cor = auto -la $    3 bash: 20161115230504 mcb @ ken007: / home / mcb / ken007 cat bin / psg.sh $    4 bash: 20161115222415 mcb @ ken007: / home / mcb / ken007 ssh ken009 $    5 bash: 20161115222450 mcb @ ken007: / home / mcb / ken007 mycommoncleanup $

    
por Malcolm Boekhoff 15.11.2016 / 13:23