Para que serve a variável $ BASH_COMMAND?

25

De acordo com o manual do Bash , a variável de ambiente BASH_COMMANDcontém

O comando atualmente sendo executado ou prestes a ser executado, a menos que o shell esteja executando um comando como resultado de uma interceptação; nesse caso, é o comando que está sendo executado no momento da interceptação.

Tirando essa caixa de canto de armadilha de lado, se entendi corretamente, isso significa que, quando executo um comando, a variável BASH_COMMANDcontém esse comando. Não está absolutamente claro se essa variável está desmarcada após a execução do comando (ou seja, só está disponível enquanto o comando está sendo executado, mas não após), embora se possa argumentar que, uma vez que é "o comando atualmente sendo executado ou prestes a ser executado" , não é o comando que acabou de ser executado.

Mas vamos verificar:

$ set | grep BASH_COMMAND=
$ 

Esvaziar. Eu esperava 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_COMMANDe, portanto, a variável BASH_COMMANDcontém a string echo $BASH_COMMAND. Por que funcionou desta vez, mas não antes?

Vamos fazer a setcoisa novamente:

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

Então espere. Ele foi definida quando eu executei que echocomando, e ele não foi desactivado depois. Mas quando eu executei setnovamente, BASH_COMMAND não estava definido para o setcomando. Não importa quantas vezes eu executo o setcomando 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 eu executei echo $BASH_COMMAND, mas não quando eu executei echo Hello AskUbuntu? Onde está a diferença agora? A variável é definida apenas quando o próprio comando atual realmente força o shell a avaliar a variável? Vamos tentar algo diferente. Talvez algum comando externo desta vez, não um bash embutido, para variar.

$ /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 é configurada apenas quando precisa ser avaliada? Por quê? Por quê? Por razões de desempenho? Vamos fazer mais uma tentativa. Vamos tentar grep for $BASH_COMMANDem um arquivo e, como $BASH_COMMANDdeve conter um grepcomando, grepgrep para esse grepcomando (ou seja, por si só). então vamos criar 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 tmpfoi expandido para grep grep $BASH_COMMAND tmp tmp(a variável é expandida apenas uma vez, é claro), e então eu esperei grep, uma vez em um arquivo $BASH_COMMANDque não existe e duas vezes no arquivo tmp.

Q1: Minha suposição atual está correta de que:

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

P2: 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 significativamente? Na verdade, eu estava tentando usá-lo $PROMPT_COMMANDpara analisar o comando que está sendo executado (e fazer algumas coisas dependendo disso), mas não posso, porque assim que, dentro do meu $PROMPT_COMMAND, eu executo um comando para examinar a variável $BASH_COMMAND, a variável obtém conjuntos para esse comando. Mesmo quando eu faço MYVARIABLE=$BASH_COMMANDlogo no início do meu $PROMPT_COMMAND, então MYVARIABLEcontém a string MYVARIABLE=$BASH_COMMAND, porque uma atribuição também é um comando. (Esta questão não é sobre como eu poderia obter o comando atual dentro de uma $PROMPT_COMMANDexecução. Existem outras maneiras, eu sei.)

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

Malte Skoruppa
fonte
5
Bom, mas provavelmente o mais adequado para unix.stackexchange.com ... existem bashüber-gurus reais lá.
Rmano
Bem, é possível movê-lo para lá? Modificações?
Malte Skoruppa
Eu realmente não tenho certeza de como fazê-lo - suponho que seja um privilégio Mod. Por outro lado, não acho sensato sinalizar - vamos ver se alguém age com a bandeira fechada.
Rmano 20/08/14
1
Para o terceiro ponto, é isso que você está procurando. Talvez com base nesse artigo, você também encontre a resposta para os dois primeiros pontos.
Radu Rădeanu
2
+1 confundiu-me, mas foi divertido de ler!
Joe

Respostas:

15

Respondendo à terceira pergunta: é claro que pode ser usado de maneira significativa no caminho do manual do Bash, com dicas claras - em uma armadilha, por exemplo:

$ 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
Dmitry Alexandrov
fonte
Ah, legal mesmo. Então você diria que uma armadilha é o único contexto em que essa variável faz sentido? Parecia uma caixa de canto no manual: "O comando que está sendo executado (...), a menos que o shell esteja executando um comando como resultado de uma armadilha, ..."
Malte Skoruppa
Depois de ler os artigos vinculados na resposta pelo @DocSalvager, fica claro que ele $BASH_COMMANDpode realmente ser usado de maneira significativa mesmo em contextos que não sejam uma armadilha. No entanto, o uso que você descreve é ​​provavelmente o mais típico e direto. Portanto, a sua resposta, e a do DocSalvager, responde melhor ao meu terceiro trimestre , e isso foi o mais importante para mim. Quanto ao Q1 e Q2, conforme indicado por @zwets, essas coisas são indefinidas. Pode ser que $BASH_COMMANDapenas seja atribuído um valor se acessado, para desempenho - ou algumas condições internas estranhas do programa podem levar a esse comportamento. Quem sabe?
Malte Skoruppa
1
Em uma nota lateral, descobri que você pode forçar $BASH_COMMANDa configuração sempre, forçando a avaliação de $BASH_COMMANDantes de cada comando. Para fazer isso, podemos usar a armadilha DEBUG, que é executada antes de cada comando (todos eles). Se emitirmos, por exemplo, trap 'echo "$BASH_COMMAND" > /dev/null' DEBUGe depois $BASH_COMMANDserão sempre avaliadas antes de cada comando (e atribuído o valor desse $COMMAND). Então, se eu executar set | grep BASH_COMMAND=enquanto essa armadilha estiver ativa ... o que você sabe? Agora, a saída é BASH_COMMAND=set, como esperado :)
Malte Skoruppa
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 ao Q1 e Q2.

A resposta para Q1 é: a correção de sua suposição é indecidível. A verdade de nenhum dos pontos principais pode ser estabelecida, pois eles perguntam sobre 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 nenhuma outra situação, ou seja, quando nenhum comando está sendo executado. Poderia ter qualquer valor ou nenhum.

A resposta para o Q2 "Se não, de que outra forma o comportamento na sequência de comandos acima pode ser explicado?" então segue logicamente (se um pouco pedanticamente): é explicado pelo fato de que o valor de BASH_COMMANDé indefinido. Como seu valor é indefinido, ele pode ter qualquer valor, exatamente o que a sequê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 eu faço MYVARIABLE = $ BASH_COMMAND logo no início do meu $ PROMPT_COMMAND, MYVARIABLE contém a sequência MYVARIABLE = $ BASH_COMMAND, porque uma atribuição também é um comando .

A maneira como leio a página de manual do bash, o bit em itálico não é verdade. A seção SIMPLE COMMAND EXPANSIONexplica como as atribuições de variáveis ​​na linha de comando são separadas e, em seguida,

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

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=setna saída de set, setessencialmente sendo um 'marcador' sintático para atribuição de variáveis.

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

Se houver um nome de comando após a expansão, a execução continuará conforme descrito abaixo. Caso contrário, o comando será encerrado.

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

zwets
fonte
1
Só porque alguma suposição não pode ser estabelecida não significa que ela esteja incorreta. Einstein assumiu que a velocidade da luz é a mesma para qualquer observador (a qualquer velocidade) como uma hipótese fundamental para a teoria da relatividade; isso não pode ser estabelecido, mas isso não significa que esteja incorreto. As propriedades "podem ser estabelecidas" e "está correta" são ortogonais. Na melhor das hipóteses, você poderia dizer "Não sabemos se está correto, pois não pode ser estabelecido". Além disso, está especificado: é o comando atual durante a execução. Então, se eu digito seteu deveria ver BASH_COMMAND='set'em algum lugar que a produção, o que
Malte Skoruppa
... no entanto, não é o caso. Mesmo que isso seja durante a execução do setcomando . Portanto, de acordo com as especificações, ele deve funcionar. Então, é que a implementação não obedece fielmente às especificações ou eu as interpreto mal? Encontrar a resposta para essa pergunta é o objetivo do primeiro trimestre. +1 para o seu postscript :)
Malte Skoruppa
@MalteSkoruppa Bons pontos. Corrigida a resposta em conformidade e adicionada uma observação sobre set.
Zwets
1
É possível que, no intérprete do bash, setnão seja um "comando". Assim como =não é um "comando". O processamento do texto após / ao redor desses tokens pode ter uma lógica totalmente diferente do processamento de um "comando".
precisa saber é o seguinte
Bons pontos de vocês dois. :) Pode ser que setisso não conte como um "comando". Eu não teria pensado que isso $BASH_COMMANDseria tão subespecificado em muitas circunstâncias. Eu acho que, pelo menos, estabelecemos que a página de manual definitivamente poderia ser mais clara sobre isso;)
Malte Skoruppa
2

Um uso inventivo para $ BASH_COMMAND

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

Esse é o principal truque do alias e substitui o uso da interceptação DEBUG. Se você leu a parte da postagem anterior sobre a interceptação DEBUG, reconhecerá a variável $ BASH_COMMAND. Nesse post, eu disse que estava definido como o texto do comando antes de cada chamada para a armadilha DEBUG. Bem, verifica-se que está definido antes da execução de cada comando, interceptação DEBUG ou não (por exemplo, execute 'echo “this command = $ BASH_COMMAND”' para ver do que estou falando). Ao atribuir a ela uma variável (apenas para essa linha), capturamos BASH_COMMAND no escopo mais externo do comando, que conterá o comando inteiro.

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

DocSalvager
fonte
Finalmente, consegui ler esses dois artigos. Uau! Que leitura! Muito esclarecedor. Esse cara é um guru do bash ! Parabéns! Isso também responde ao meu comentário sobre a resposta de Dmitry sobre se $BASH_COMMANDpode ser usado significativamente em um contexto que não seja a trap. E que utilidade é essa! Usando-o para implementar comandos macro no bash - quem teria pensado isso possível? Obrigado pelo ponteiro.
Malte Skoruppa
0

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

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

Eu descobri que o método usando PROMPT_COMMAND para enviar a "sequência de título nulo" não funcionou tão bem, então reverti para o método trap ... debug.

Aqui está como você faz isso:

1 Diga à "tela" para procurar a sequência de escape (ative-a) colocando "shelltitle '$ | bash:'" em "$ HOME / .screenrc".

2 Desative a propagação da armadilha de depuração em subcascas. Isso é importante porque, se estiver ativado, tudo fica estragado, use: "set + o functrace".

3 Envie a sequência de escape do título para "tela" para interpretar: trap 'printf "\ ek $ (data +% 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 de número

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 --color = auto-la $ 3 bash: 20161115230504 mcb @ ken007: / home / mcb / ken007 bin bin / psg.sh $ 4 bash: 20161115222415 mcb @ ken007: / home / mcb / ken007 ssh ken009 $ 5 bash: 20161115222450 mcb @ ken007: / home / mcb / ken007 mycommoncleanup $

Malcolm Boekhoff
fonte