A cosa serve la variabile $ BASH_COMMAND?


25

Secondo il manuale di Bash , la variabile di ambiente BASH_COMMANDcontiene

Il comando attualmente in esecuzione o che sta per essere eseguito, a meno che la shell non stia eseguendo un comando come risultato di una trap, nel qual caso è il comando in esecuzione al momento della trap.

Prendendo da parte il caso dell'angolo trap, se ho capito bene questo significa che quando eseguo un comando, la variabile BASH_COMMANDcontiene quel comando. Non è assolutamente chiaro se quella variabile non sia impostata dopo l'esecuzione del comando (ovvero, è disponibile solo mentre il comando è in esecuzione, ma non dopo), anche se si potrebbe sostenere che poiché è "il comando attualmente in esecuzione o sta per essere eseguito" , non è il comando che è stato appena eseguito.

Ma controlliamo:

$ set | grep BASH_COMMAND=
$ 

Vuoto. Mi sarei aspettato di vedere BASH_COMMAND='set | grep BASH_COMMAND='o forse solo BASH_COMMAND='set', ma il vuoto mi ha sorpreso.

Proviamo qualcos'altro:

$ echo $BASH_COMMAND
echo $BASH_COMMAND
$ 

Beh, ha senso. Eseguo il comando echo $BASH_COMMANDe quindi la variabile BASH_COMMANDcontiene la stringa echo $BASH_COMMAND. Perché questa volta ha funzionato, ma non prima?

Facciamo di setnuovo la cosa:

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

Quindi aspetta. È stato impostato quando ho eseguito quel echocomando e in seguito non è stato disattivato. Ma quando ho eseguito di setnuovo, BASH_COMMAND non è stato impostato il setcomando. Non importa quante volte eseguo il setcomando qui, il risultato rimane lo stesso. Quindi, la variabile è impostata durante l'esecuzione echo, ma non durante l'esecuzione set? Vediamo.

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

Che cosa? Quindi la variabile è stata impostata al momento dell'esecuzione echo $BASH_COMMAND, ma non al momento dell'esecuzione echo Hello AskUbuntu? Dov'è la differenza adesso? La variabile è impostata solo quando l'attuale comando stesso impone effettivamente alla shell di valutare la variabile? Proviamo qualcosa di diverso. Forse un comando esterno questa volta, non un built-in bash, per cambiare.

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

Hmm, ok ... di nuovo, la variabile è stata impostata. Quindi la mia ipotesi attuale è corretta? La variabile è impostata solo quando deve essere valutata? Perché? Perché? Per motivi di prestazioni? Facciamo un altro tentativo. Cercheremo di eseguire grep $BASH_COMMANDin un file, e poiché $BASH_COMMANDquindi dovrebbe contenere un grepcomando, grepdovrebbe grep per quel grepcomando (cioè, per se stesso). quindi facciamo un file appropriato:

$ 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. Il comando è grep $BASH_COMMAND tmpstato espanso in grep grep $BASH_COMMAND tmp tmp(la variabile si espande solo una volta, ovviamente), e quindi ho cercato grep, una volta in un file $BASH_COMMANDche non esiste e due volte nel file tmp.

Q1: Il mio presupposto attuale è corretto che:

  • BASH_COMMANDviene impostato solo quando un comando tenta di valutarlo effettivamente; e
  • esso è non disinserito dopo l'esecuzione di un comando, anche se la descrizione può portarci a credere così?

Q2: Se sì, perché? Prestazione? In caso negativo, in che altro modo è possibile spiegare il comportamento nella sequenza di comandi sopra?

Q3: Infine, esiste uno scenario in cui questa variabile potrebbe essere effettivamente utilizzata in modo significativo? In realtà stavo cercando di usarlo all'interno $PROMPT_COMMANDper analizzare il comando in esecuzione (e fare alcune cose a seconda di quello), ma non posso, perché non appena, all'interno del mio $PROMPT_COMMAND, eseguo un comando per guardare la variabile $BASH_COMMAND, la variabile viene impostato su quel comando. Anche quando faccio MYVARIABLE=$BASH_COMMANDbene all'inizio del mio $PROMPT_COMMAND, allora MYVARIABLEcontiene la stringa MYVARIABLE=$BASH_COMMAND, perché anche un compito è un comando. (Questa domanda non riguarda come ho potuto ottenere il comando corrente all'interno di $PROMPT_COMMANDun'esecuzione. Ci sono altri modi, lo so.)

È un po 'come con il principio di incertezza di Heisenberg. Solo osservando la variabile, la cambio.


5
Bello, ma probabilmente più adatto a unix.stackexchange.com ... ci sono veri bashüber-guru lì.
Rmano,

Bene, è possibile spostarlo laggiù? Mods?
Malte Skoruppa,

Non sono davvero sicuro di come farlo --- Suppongo che sia un privilegio Mod. D'altra parte non penso che contrassegnarlo sia sensato --- vediamo se qualcuno agisce sulla bandiera chiusa.
Rmano,

1
Per il terzo punto, questo è ciò che stai cercando. Forse in base a quell'articolo puoi trovare la risposta anche per i primi due punti.
Radu Rădeanu,

2
+1 mi ha confuso, ma è stato divertente da leggere!
Joe,

Risposte:


15

Rispondere alla terza domanda: ovviamente può essere utilizzato in modo significativo nel modo in cui il manuale di Bash suggerisce chiaramente - in una trappola, ad esempio:

$ 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

Ah, davvero carino. Quindi diresti che una trappola è l'unico contesto in cui questa variabile ha senso? Sembrava un caso angolare nel manuale: "Il comando in esecuzione (...), a meno che la shell non stia eseguendo un comando come risultato di una trappola, ..."
Malte Skoruppa

Dopo aver letto gli articoli collegati nella risposta di @DocSalvager, è chiaro che $BASH_COMMANDpossono effettivamente essere utilizzati in modo significativo anche in contesti diversi da una trappola. Tuttavia, l'uso che descrivi è probabilmente il più tipico e diretto. Quindi la tua risposta, e quella di DocSalvager, rispondono meglio al mio Q3 , e questa è stata la più importante per me. Per quanto riguarda Q1 e Q2, come indicato da @zwets, queste cose non sono definite. Può darsi che $BASH_COMMANDvenga dato un valore solo se vi si accede, per prestazioni - oppure alcune strane condizioni interne del programma possono portare a quel comportamento. Chissà?
Malte Skoruppa,

1
In un sidenote, ho scoperto che puoi forzare $BASH_COMMANDl'impostazione sempre, forzando la valutazione di $BASH_COMMANDprima di ogni comando. Per fare ciò, possiamo usare la trap DEBUG, che viene eseguita prima di ogni singolo comando (tutti). Se emettiamo, ad esempio, trap 'echo "$BASH_COMMAND" > /dev/null' DEBUGallora $BASH_COMMANDsarà sempre valutato prima di ogni comando (e assegnato il valore di quello $COMMAND). Quindi se eseguo set | grep BASH_COMMAND=mentre quella trappola è attiva ... cosa sai? Ora l'output è BASH_COMMAND=set, come previsto :)
Malte Skoruppa,

6

Ora che Q3 ha avuto risposta (correttamente, secondo me: BASH_COMMANDè utile nelle trappole e quasi da nessun'altra parte), diamo una possibilità a Q1 e Q2.

La risposta a Q1 è: la correttezza del tuo presupposto è indecidibile. La verità di nessuno dei punti elenco può essere stabilita, poiché chiedono comportamenti non specificati. In base alle sue specifiche, il valore di BASH_COMMANDè impostato sul testo di un comando per la durata dell'esecuzione di quel comando. La specifica non indica quale deve essere il suo valore in qualsiasi altra situazione, ovvero quando non viene eseguito alcun comando. Potrebbe avere qualsiasi valore o nessuno.

La risposta a Q2 "Se no, in che altro modo si può spiegare il comportamento nella sequenza di comandi sopra?" quindi segue logicamente (se un po 'pedanticamente): è spiegato dal fatto che il valore di BASH_COMMANDnon è definito. Poiché il suo valore non è definito, può avere qualsiasi valore, che è esattamente ciò che mostra la sequenza.

poscritto

C'è un punto in cui penso che stai davvero colpendo un punto debole nelle specifiche. È dove dici:

Anche quando faccio MYVARIABLE = $ BASH_COMMAND all'inizio del mio $ PROMPT_COMMAND, MYVARIABLE contiene la stringa MYVARIABLE = $ BASH_COMMAND, perché anche un compito è un comando .

Il modo in cui leggo la pagina man di bash, il bit in corsivo non è vero. La sezione SIMPLE COMMAND EXPANSIONspiega come vengono prima messe da parte le assegnazioni delle variabili sulla riga di comando e quindi

Se non risulta alcun nome di comando [in altre parole, c'erano solo assegnazioni di variabili], le assegnazioni di variabili influiscono sull'ambiente di shell corrente.

Questo mi suggerisce che le assegnazioni di variabili non sono comandi (e quindi esenti dal presentarsi in BASH_COMMAND), come in altri linguaggi di programmazione. Questo spiegherebbe anche perché non c'è una linea BASH_COMMAND=setnell'output di set, setessendo essenzialmente un 'marker' sintattico per l'assegnazione variabile.

OTOH, nel paragrafo finale di quella sezione dice

Se dopo l'espansione è presente un nome comando, l'esecuzione procede come descritto di seguito. Altrimenti, il comando termina.

... che suggerisce il contrario, e anche le assegnazioni di variabili sono comandi.


1
Solo perché non è possibile stabilire un presupposto non significa che sia errato. Einstein ipotizzò che la velocità della luce fosse la stessa per qualsiasi osservatore (a qualsiasi velocità) come un'ipotesi fondamentale per la teoria della relatività; ciò non può essere stabilito, ma ciò non significa che sia errato. Le proprietà "possono essere stabilite" e "è corretta" sono ortogonali. Nella migliore delle ipotesi potresti dire "Non sappiamo se sia corretto, dal momento che non può essere stabilito". Inoltre, è specificato: è il comando corrente durante l'esecuzione. Quindi, se setBASH_COMMAND='set'
scrivo,

... comunque non è il caso. Anche se ciò avviene durante l'esecuzione del setcomando . Quindi, secondo le specifiche, dovrebbe funzionare. Quindi l'implementazione non obbedisce fedelmente alle specifiche, o fraintendo le specifiche? Trovare la risposta a questa domanda è un po 'lo scopo di Q1. +1 per il tuo post scriptum :)
Malte Skoruppa

@MalteSkoruppa Punti positivi. Risolto il problema di conseguenza, e aggiunto un commento su set.
zwets,

1
È possibile che nell'interprete bash setnon sia un "comando". Proprio come =non è un "comando". L'elaborazione del testo che segue / circonda quei token potrebbe avere una logica completamente diversa dall'elaborazione di un "comando".
DocSalvager,

Buoni punti da entrambi. :) Può darsi che setnon sia considerato un "comando". Non avrei pensato che si $BASH_COMMANDsarebbe rivelato così poco specificato in molte circostanze. Immagino almeno che abbiamo stabilito che la pagina man sicuramente potrebbe essere più chiara al riguardo;)
Malte Skoruppa

2

Un uso inventivo per $ BASH_COMMAND

Recentemente ho trovato questo straordinario uso di $ BASH_COMMAND nell'implementazione di una funzionalità macro-simile.

Questo è il trucco principale dell'alias e sostituisce l'uso della trap DEBUG. Se leggi la parte nel post precedente sulla trap DEBUG, riconoscerai la variabile $ BASH_COMMAND. In quel post ho detto che era impostato sul testo del comando prima di ogni chiamata alla trap DEBUG. Bene, si scopre che è impostato prima dell'esecuzione di ogni comando, DEBUG trap o no (es. Eseguire 'echo “questo comando = $ BASH_COMMAND”' per vedere di cosa sto parlando). Assegnandole una variabile (solo per quella riga) catturiamo BASH_COMMAND nell'ambito più esterno del comando, che conterrà l'intero comando.

Il precedente articolo dell'autore fornisce anche un buon background durante l'implementazione di una tecnica usando un DEBUG trap. La trapviene eliminato nella versione migliorata.


+1 Sono finalmente riuscito a leggere quei due articoli. Wow! Che lettura! Molto illuminante. Quel ragazzo è un guru bash ! Complimenti! Questo risponde anche al mio commento sulla risposta di Dmitry sull'opportunità di $BASH_COMMANDutilizzare in modo significativo in un contesto diverso da a trap. E che utilità! Usandolo per implementare i comandi macro in bash - chi avrebbe mai pensato che fosse possibile? Grazie per il puntatore.
Malte Skoruppa,

0

Un uso apparentemente comune per il trap ... debug è migliorare i titoli che compaiono nella window list (^ A ") quando si utilizza" screen ".

Stavo cercando di rendere più utilizzabile lo "schermo", la "lista finestre", quindi ho iniziato a trovare articoli che fanno riferimento a "trap ... debug".

Ho scoperto che il metodo che utilizza PROMPT_COMMAND per inviare la "sequenza di titoli null" non ha funzionato così bene, quindi sono tornato al metodo trap ... debug.

Ecco come lo fai:

1 Dì a "schermo" di cercare la sequenza di escape (abilitalo) mettendo "shelltitle '$ | bash:'" in "$ HOME / .screenrc".

2 Disattivare la propagazione della trap di debug in sotto-shell. Questo è importante, perché se è abilitato, tutto viene confuso, utilizzare: "set + o functrace".

3 Invia la sequenza di escape del titolo per "schermo" da interpretare: trap 'printf "\ ek $ (data +% Y% m% d% H% M% S) $ (whoami) @ $ (nome host): $ (pwd) $ {BASH_COMMAND} \ e \ "'" DEBUG "

Non è perfetto, ma aiuta e puoi usare questo metodo per inserire letteralmente tutto ciò che ti piace nel titolo

Vedi il seguente "elenco finestre" (5 schermate):

Bandiere nome num

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 cat bin / psg.sh $ 4 bash: 20161115222415 mcb @ ken007: / home / mcb / ken007 ssh ken009 $ 5 bash: 20161115222450 mcb @ ken007: / home / mcb / ken007 mycommoncleanup $

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.