Entsprechend derBash-Handbuch, die Umgebungsvariable BASH_COMMAND
enthä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_COMMAND
diesen 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_COMMAND
und die Variable BASH_COMMAND
enthält die Zeichenfolge echo $BASH_COMMAND
. Warum hat es dieses Mal funktioniert, aber vorher nicht?
Lass uns das set
Ganze noch einmal machen:
$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$
Also warte. EsWargesetzt, als ich diesen echo
Befehl ausführte, und eswar nichtdanach wieder aufgehoben. Aber als ich es set
erneut ausführte,BASH_COMMAND
war nichtauf den Befehl gesetzt set
. Egal wie oft ich den set
Befehl 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_COMMAND
in einer Datei nach zu suchen, und da diese einen Befehl $BASH_COMMAND
enthalten sollte , sollten wir nach diesem Befehl (also nach sich selbst) suchen. Erstellen wir also eine entsprechende Datei:grep
grep
grep
$ 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 tmp
wurde 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_COMMAND
wird 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_COMMAND
um den ausgeführten Befehl zu analysieren (und davon abhängig einige Dinge zu tun), aber das geht nicht, denn sobald $PROMPT_COMMAND
ich 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_COMMAND
gleich zu Beginn meines mache $PROMPT_COMMAND
, enthält dann MYVARIABLE
die 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_COMMAND
Ausfü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_COMMAND
Sie 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_COMMAND
wird 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_COMMAND
undefiniert 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 EXPANSION
erklä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=set
in der Ausgabe von keine Zeile gibt set
, die set
im 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 trap
wurde 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 $