Wofür ist die Variable $BASH_COMMAND gut?

Wofür ist die Variable $BASH_COMMAND gut?

Entsprechend derBash-Handbuch, die Umgebungsvariable BASH_COMMANDenthält

Der Befehl, der aktuell ausgeführt wird oder ausgeführt werden soll, es sei denn, die Shell führt einen Befehl als Ergebnis eines Traps aus. In diesem Fall handelt es sich um den Befehl, der zum Zeitpunkt des Traps ausgeführt wird.

Wenn ich diesen Trap-Corner-Fall einmal beiseite lasse, bedeutet das, wenn ich das richtig verstehe, dass die Variable BASH_COMMANDdiesen Befehl enthält, wenn ich ihn ausführe. Es ist nicht ganz klar, ob diese Variable nach der Befehlsausführung gelöscht wird (d. h. nur verfügbar istwährendder Befehl ausgeführt wird, aber nicht danach), obwohl man argumentieren könnte, dass, da es sich um "den Befehlmomentanausgeführt wird oderim Begriff zu seinausgeführt", ist es nicht der Befehldas war geradehingerichtet.

Aber schauen wir mal nach:

$ set | grep BASH_COMMAND=
$ 

Leer. Ich hätte erwartet, BASH_COMMAND='set | grep BASH_COMMAND='oder vielleicht nur zu sehen BASH_COMMAND='set', aber leer hat mich überrascht.

Versuchen wir etwas anderes:

$ echo $BASH_COMMAND
echo $BASH_COMMAND
$ 

Das macht Sinn. Ich führe den Befehl aus echo $BASH_COMMANDund die Variable BASH_COMMANDenthält die Zeichenfolge echo $BASH_COMMAND. Warum hat es dieses Mal funktioniert, aber vorher nicht?

Lass uns das setGanze noch einmal machen:

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

Also warte. EsWargesetzt, als ich diesen echoBefehl ausführte, und eswar nichtdanach wieder aufgehoben. Aber als ich es seterneut ausführte,BASH_COMMAND war nichtauf den Befehl gesetzt set. Egal wie oft ich den setBefehl hier ausführe, das Ergebnis bleibt das gleiche. Wird die Variable also beim Ausführen gesetzt echo, aber nicht beim Ausführen set? Mal sehen.

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

Was?Die Variable wurde also gesetzt, als ich sie ausführte echo $BASH_COMMAND, abernichtals ich ausgeführt habe echo Hello AskUbuntu? Wo ist jetzt der Unterschied? Wird die Variable nur gesetzt, wenn der aktuelle Befehl selbst die Shell tatsächlich dazu zwingt, die Variable auszuwerten? Versuchen wir etwas anderes. Vielleicht diesmal zur Abwechslung ein externer Befehl, kein integriertes Bash-Befehl.

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

Hmm, ok... nochmal, die Variable wurde gesetzt. Ist meine aktuelle Vermutung also richtig? Wird die Variable nur gesetzt, wenn sie ausgewertet werden muss? Warum?Warum?Aus Leistungsgründen? Machen wir noch einen Versuch. Wir versuchen, $BASH_COMMANDin einer Datei nach zu suchen, und da diese einen Befehl $BASH_COMMANDenthalten sollte , sollten wir nach diesem Befehl (also nach sich selbst) suchen. Erstellen wir also eine entsprechende Datei:grepgrepgrep

$ 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, interessant. Der Befehl grep $BASH_COMMAND tmpwurde auf erweitert grep grep $BASH_COMMAND tmp tmp(die Variable wird natürlich nur dieses eine Mal erweitert), und so habe ich nach gesucht grep, einmal in einer Datei $BASH_COMMAND, die nicht existiert, und zweimal in der Datei tmp.

Frage 1:Ist meine derzeitige Annahme richtig, dass:

  • BASH_COMMANDwird nur gesetzt, wenn ein Befehl versucht, es tatsächlich auszuwerten; und
  • es istnichtnach der Ausführung eines Befehls aufgehoben, obwohl die Beschreibung dies vermuten lässt?

Frage 2:Wenn ja, warum? Leistung? Wenn nein, wie lässt sich das Verhalten in der obigen Befehlssequenz sonst erklären?

Frage 3:Und schließlich: Gibt es ein Szenario, in dem diese Variable tatsächlich sinnvoll verwendet werden könnte? Ich habe eigentlich versucht, sie zu verwenden, $PROMPT_COMMANDum den ausgeführten Befehl zu analysieren (und davon abhängig einige Dinge zu tun), aber das geht nicht, denn sobald $PROMPT_COMMANDich innerhalb meines einen Befehl ausführe, um die Variable anzusehen $BASH_COMMAND, wird die Variable auf diesen Befehl gesetzt. Selbst wenn ich das MYVARIABLE=$BASH_COMMANDgleich zu Beginn meines mache $PROMPT_COMMAND, enthält dann MYVARIABLEdie Zeichenfolge MYVARIABLE=$BASH_COMMAND, weil eine Zuweisung auch ein Befehl ist. (Diese Frage bezieht sich nicht darauf, wie ich den aktuellen Befehl innerhalb einer $PROMPT_COMMANDAusführung erhalten könnte. Ich weiß, dass es andere Möglichkeiten gibt.)

Es ist ein bisschen wie mit Heisenbergs Unschärferelation. Allein durch die Beobachtung der Variable ändere ich sie.

Antwort1

Antwort auf die dritte Frage: Natürlich kann es auch sinnvoll verwendet werden, wie es im Bash-Handbuch klar angedeutet wird – in einer Falle, z. B.:

$ 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

Antwort2

Nachdem Frage 3 nun beantwortet wurde (und zwar richtig, meiner Meinung nach: BASH_COMMANDSie ist in Fallen nützlich, aber sonst kaum irgendwo), versuchen wir es mit Frage 1 und Frage 2.

Die Antwort auf Frage 1 lautet: Die Richtigkeit Ihrer Annahme ist nicht entscheidbar. Die Wahrheit von keinem der Aufzählungspunkte kann festgestellt werden, da sie nach nicht spezifiziertem Verhalten fragen. Durch die Spezifikation BASH_COMMANDwird der Wert von für die Dauer der Ausführung dieses Befehls auf den Text eines Befehls gesetzt. Die Spezifikation gibt nicht an, welchen Wert der Wert in einer anderen Situation haben muss, d. h. wenn kein Befehl ausgeführt wird. Er könnte jeden beliebigen Wert haben oder auch gar keinen.

Die Antwort auf Frage 2 „Wenn nein, wie lässt sich das Verhalten in der obigen Befehlssequenz sonst erklären?“ folgt dann logisch (wenn auch etwas pedantisch): Es erklärt sich dadurch, dass der Wert von BASH_COMMANDundefiniert ist. Da sein Wert undefiniert ist, kann es jeden beliebigen Wert annehmen, und genau das zeigt die Sequenz.

Nachtrag

An einer Stelle stoßen Sie meiner Meinung nach tatsächlich auf eine Schwachstelle der Spezifikation. Dort sagen Sie:

Selbst wenn ich MYVARIABLE=$BASH_COMMAND direkt am Anfang meines $PROMPT_COMMAND eingebe, enthält MYVARIABLE die Zeichenfolge MYVARIABLE=$BASH_COMMAND.denn auch ein Auftrag ist ein Befehl.

So wie ich die Bash-Manpage lese, stimmt der kursiv gedruckte Teil nicht. Der Abschnitt SIMPLE COMMAND EXPANSIONerklärt, wie zuerst die Variablenzuweisungen auf der Kommandozeile aufgehoben werden und dann

Wenn sich dabei kein Kommandoname ergibt [also nur Variablenzuweisungen erfolgten], dann wirken sich die Variablenzuweisungen auf die aktuelle Shell-Umgebung aus.

Dies lässt mich vermuten, dass Variablenzuweisungen keine Befehle sind (und daher nicht in vorkommen dürfen BASH_COMMAND), wie in anderen Programmiersprachen. Dies würde dann auch erklären, warum es BASH_COMMAND=setin der Ausgabe von keine Zeile gibt set, die setim Wesentlichen ein syntaktischer „Marker“ für die Variablenzuweisung ist.

Andererseits heißt es im letzten Absatz dieses Abschnitts:

Wenn nach der Erweiterung noch ein Befehlsname übrig ist, wird die Ausführung wie unten beschrieben fortgesetzt. Andernfalls wird derBefehlAusgänge.

... was etwas anderes nahelegt, und Variablenzuweisungen sind auch Befehle.

Antwort3

Eine erfinderische Verwendung für $BASH_COMMAND

Kürzlich gefundenbeeindruckende Verwendung von $BASH_COMMANDbei der Implementierung einer makroähnlichen Funktionalität.

Dies ist der Kerntrick des Alias ​​und ersetzt die Verwendung der DEBUG-Trap. Wenn Sie den Teil im vorherigen Beitrag über die DEBUG-Trap gelesen haben, werden Sie die Variable $BASH_COMMAND wiedererkennen. In diesem Beitrag sagte ich, dass sie vor jedem Aufruf der DEBUG-Trap auf den Text des Befehls gesetzt wurde. Nun, es stellt sich heraus, dass sie vor der Ausführung jedes Befehls gesetzt wird, ob mit oder ohne DEBUG-Trap (führen Sie beispielsweise „echo „this command = $BASH_COMMAND“ aus, um zu sehen, wovon ich spreche). Indem wir ihr eine Variable zuweisen (nur für diese Zeile), erfassen wir BASH_COMMAND im äußersten Bereich des Befehls, der den gesamten Befehl enthalten wird.

Die AutorenVorheriger Artikelbietet auch einige gute Hintergrundinformationen zur Implementierung einer Technik mit einem DEBUG trap. Dies trapwurde in der verbesserten Version eliminiert.

Antwort4

Eine scheinbar häufige Verwendung für Trap...Debug besteht darin, die Titel zu verbessern, die in der Fensterliste (^A") angezeigt werden, wenn Sie „Bildschirm“ verwenden.

Ich habe versucht, den „Bildschirm“ und die „Fensterliste“ benutzerfreundlicher zu gestalten, und habe daher Artikel mit Verweisen auf „Trap...Debug“ gefunden.

Ich habe festgestellt, dass die Methode mit PROMPT_COMMAND zum Senden der „Null-Titelsequenz“ nicht so gut funktionierte, daher bin ich zur Trap...Debug-Methode zurückgekehrt.

So geht's:

1 Weisen Sie „screen“ an, nach der Escape-Sequenz zu suchen (aktivieren Sie sie), indem Sie „shelltitle '$|bash:'“ in „$HOME/.screenrc“ einfügen.

2 Schalten Sie die Ausbreitung des Debug-Traps in Unter-Shells aus. Das ist wichtig, denn wenn es aktiviert ist, gerät alles durcheinander. Verwenden Sie: „set +o functrace“.

3 Senden Sie die Titel-Escape-Sequenz, damit „screen“ sie interpretieren kann: trap 'printf "\ek$(date +%Y%m%d%H%M%S) $(whoami)@$(hostname):$(pwd) ${BASH_COMMAND}\e\"' "DEBUG"

Es ist nicht perfekt, aber es hilft, und Sie können diese Methode verwenden, um buchstäblich alles, was Sie möchten, in den Titel einzufügen

Siehe die folgende „Fensterliste“ (5 Bildschirme):

Nummernnamen-Flags

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 $

verwandte Informationen