De acordo com o manual do 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 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_COMMAND
conté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_COMMAND
e, portanto, a variável BASH_COMMAND
contém a string echo $BASH_COMMAND
. Por que funcionou desta vez, mas não antes?
Vamos fazer a set
coisa novamente:
$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$
Então espere. Ele foi definida quando eu executei que echo
comando, e ele não foi desactivado depois. Mas quando eu executei set
novamente, BASH_COMMAND
não estava definido para o set
comando. Não importa quantas vezes eu executo o set
comando 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_COMMAND
em um arquivo e, como $BASH_COMMAND
deve conter um grep
comando, grep
grep para esse grep
comando (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 tmp
foi 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_COMMAND
que 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_COMMAND
para 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_COMMAND
logo 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. (Esta questão não é sobre como eu poderia obter o comando atual dentro de uma $PROMPT_COMMAND
execução. Existem outras maneiras, eu sei.)
É um pouco como no princípio da incerteza de Heisenberg. Apenas observando a variável, eu a altero.
fonte
bash
über-gurus reais lá.Respostas:
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:
fonte
$BASH_COMMAND
pode 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_COMMAND
apenas seja atribuído um valor se acessado, para desempenho - ou algumas condições internas estranhas do programa podem levar a esse comportamento. Quem sabe?$BASH_COMMAND
a configuração sempre, forçando a avaliação de$BASH_COMMAND
antes 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' DEBUG
e depois$BASH_COMMAND
serão sempre avaliadas antes de cada comando (e atribuído o valor desse$COMMAND
). Então, se eu executarset | grep BASH_COMMAND=
enquanto essa armadilha estiver ativa ... o que você sabe? Agora, a saída éBASH_COMMAND=set
, como esperado :)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:
A maneira como leio a página de manual do bash, o bit em itálico não é verdade. A seção
SIMPLE COMMAND EXPANSION
explica como as atribuições de variáveis na linha de comando são separadas e, em seguida,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 linhaBASH_COMMAND=set
na saída deset
,set
essencialmente sendo um 'marcador' sintático para atribuição de variáveis.OTOH, no parágrafo final dessa seção, ele diz
... o que sugere o contrário, e atribuições de variáveis também são comandos.
fonte
set
eu deveria verBASH_COMMAND='set'
em algum lugar que a produção, o queset
comando . 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 :)set
.set
nã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".set
isso não conte como um "comando". Eu não teria pensado que isso$BASH_COMMAND
seria 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;)Um uso inventivo para $ BASH_COMMAND
Recentemente, encontrei esse uso impressionante de $ BASH_COMMAND na implementação de uma funcionalidade semelhante a macro.
O artigo anterior do autor também fornece bons antecedentes ao implementar uma técnica usando um DEBUG
trap
. Otrap
é eliminado na versão aprimorada.fonte
$BASH_COMMAND
pode ser usado significativamente em um contexto que não seja atrap
. E que utilidade é essa! Usando-o para implementar comandos macro no bash - quem teria pensado isso possível? Obrigado pelo ponteiro.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 $
fonte