Was ist das letzte Argument des vorherigen Befehls?

12

$_ soll das letzte Argument des vorherigen Befehls sein.

Also frage ich mich, warum es nicht so ist, EDITOR="emacs -nw"aber EDITORim folgenden Beispiel?

Warum ist das nicht "emacs -nw"Teil des letzten Arguments?

Was sind allgemein die Definitionen eines Arguments und das letzte Argument?

Vielen Dank.

$ export EDITOR="emacs -nw"
$ echo $_
EDITOR
Tim
quelle
3
Ich denke, es ist aus dem gleichen Grund, dass Shellcheck Ihnen sagt, dass Sie Variablen nicht in derselben Zeile exportieren sollen, in der Sie sie zuweisen. Die Zuordnung erfolgt und dann wird die Variable exportiert. EDITORist ein Argument zum Exportieren
jesse_b
FWIW pdkshund dashenthält den zugewiesenen Wert, ksh93verhält sich jedoch wie bashfolgt.
Kusalananda
zsh : export FOO=bar; echo $_, druckt export.
Ilkkachu
@Jesse_b Das Ganze ist das letzte Argument / der letzte Operand (einschließlich des zugewiesenen Werts), aber es kann etwas mit der Tatsache zu tun haben, dass exportes sich um ein integriertes Dienstprogramm handelt.
Kusalananda
ksh: druckt typeset -x FOO=bardann , aber in Bash druckt . echo $_FOOdeclare -x FOO=bar; echo $_FOO=bar
Ilkkachu

Antworten:

13

Bash verarbeitet Variablenzuweisungen, wenn sie als Argumente zulässig sind (mit alias, declare, export, local, readonly, und typeset), bevor irgendetwas anderes (oder besser gesagt, es ihnen , bevor irgendetwas identifiziert andere - Erweiterung der Variablen zugewiesen Werte gilt). Wenn es um die Worterweiterung geht export EDITOR, _ist der verbleibende Befehl , also auf gesetzt EDITOR.

Im Allgemeinen sind Argumente die „Wörter“, die nach der Erweiterung verbleiben (ohne Variablenzuweisungen und Umleitungen).

Weitere Informationen finden Sie unter Einfache Befehlserweiterung im Bash-Handbuch.

Stephen Kitt
quelle
Und mir ist klar declare, dass sein Verhalten nicht mit dem übereinstimmt, was ich beschreibe ...
Stephen Kitt
Nun, das ist nicht sehr konsequent. declare a=b; echo $_Drucke a=b; export c=d; echo $_druckt nur c. aliasscheint nur den Namen zu drucken, localdruckt andererseits das ganze Argument. Und readonlyauch druckt nur den Namen, die ich ein bisschen fand überraschend , da ich gedacht hätte readonlyund localwürde ähnlich sein declare.
Ilkkachu
1
@ilkkachu heh, das habe ich auch gemerkt (siehe oben). exportund readonlysind zusammen in deklariert setattr.def, declare, local, und typesetin deklariert sind declare.def, aliassteht allein in alias.def.
Stephen Kitt
Vielen Dank. Wenn Variablenzuweisungen als Argumente für einige Befehle verwendet werden (1) "(oder besser gesagt, sie werden vor allem anderen identifiziert - die Erweiterung gilt für die Werte, die Variablen zugewiesen sind)", bedeutet dies, dass die Werte vor der Variablenzuweisung erweitert werden? (2) "Wenn es um die Worterweiterung geht, lautet der verbleibende Befehl export EDITOR". Bedeutet dies, dass die Variablenzuweisung vor der Erweiterung erfolgt? Die beiden Zitate scheinen sich zu widersprechen.
Tim
Vielen Dank. Ich bin ein bisschen verwirrt. Wenn Variablenzuweisungen als Argumente für alias, declare, export, local, readonlyund verwendet werden typeset. Was passiert zuerst und als nächstes? "Wenn es um die Worterweiterung geht, lautet der verbleibende Befehl export EDITOR". Bedeuten Sie, dass die Variablenzuweisung EDITOR="emacs -nw"vor der Erweiterung erfolgt? Wenn nicht, warum enthält der verbleibende Befehl die Zuweisung nicht als Argument? Wenn ja, müssen die den Variablen zugewiesenen Werte nicht erweitert werden, bevor die Variablenzuweisung durchgeführt wird?
Tim
4

TL; DR: Im Fall von export FOO=barruft bash seine temporäre Umgebungserstellung auf, setzt FOO=barin dieser Umgebung und gibt dann einen endgültigen Befehl von aus export FOO. An dieser Stelle FOOwird als letztes Argument genommen.


Ah, der viel missbrauchte $_:

($ _, ein Unterstrich.) Legen Sie beim Shell-Start den absoluten Pfadnamen fest, der zum Aufrufen der Shell oder des Shell-Skripts verwendet wird, die ausgeführt werden, wie in der Umgebungs- oder Argumentliste übergeben. Erweitert anschließend nach der Erweiterung zum letzten Argument des vorherigen Befehls. Stellen Sie auch den vollständigen Pfadnamen ein, der zum Aufrufen jedes ausgeführten und in die in diesen Befehl exportierte Umgebung platzierten Befehls verwendet wird. Beim Überprüfen von E-Mails enthält dieser Parameter den Namen der E-Mail-Datei.

Schauen wir uns einige Variationen an:

$ man; echo $_
What manual page do you want?
man
$ man foo; echo $_
No manual entry for foo
foo
$ echo; echo $_

echo
$ echo bar foo; echo $_
bar foo
foo
$ foo=x eval 'echo $foo'; echo $_
x
echo $foo
$ bar() { man $1; }; echo $_
foo
$ for (( i=0; $i<0; i=i+1 )); do echo $i; done; echo $_
foo
$ bar; echo $_
What manual page do you want?
man
$ bar foo; echo $_
No manual entry for foo
foo
$ MANPATH=/tmp; echo $_

$ export MANPATH=/tmp; echo $_
MANPATH

Wir sehen hier also drei Muster:

  • Vom Dateisystem aufgerufene Befehle, Funktionen und integrierte Funktionen verhalten sich wie allgemein erwartet: $_Wird auf den Befehlsnamen selbst gesetzt, wenn keine Argumente vorhanden sind, andernfalls das letzte der dargestellten Argumente.
  • Nach Funktionsdefinitionen, Schleifen und anderen logischen Konstruktionen: $_wird nicht geändert.
  • Alles andere: $_ist auf etwas eingestellt, was nicht ganz erwartet wird; seltsam.

Ich habe den Code instrumentiert, um einen Einblick in die Verrücktheit zu geben.

$ ./bash --noprofile --norc -c 'man foo'
lastword=[man]
lastarg=[foo]
$ ./bash --noprofile --norc -c 'export FOO=bar'
lastword=[export]
lastarg=[FOO=bar]
bind_variable, name=[FOO], value=[bar]
before bind_lastarg, lastarg=[FOO]
bind_lastarg, arg=[FOO]
bind_variable, name=[_], value=[FOO]
$ ./bash --noprofile --norc -c 'declare FOO=bar'
lastword=[declare]
lastarg=[FOO=bar]
bind_variable, name=[FOO], value=[(null)]
before bind_lastarg, lastarg=[FOO=bar]
bind_lastarg, arg=[FOO=bar]
bind_variable, name=[_], value=[FOO=bar]

Sie können sehen, dass der Parserlastarg= in allen Fällen das erwartete letzte Argument ( ) sieht , aber was danach passiert, hängt davon ab, was Bash für richtig hält. Siehe execute_cmd.c, execute_simple_command () .

Im Fall von export FOO=barnimmt bash die Zuweisung vor und exportiert dann die Variable. Dies scheint im Einklang mit der Behauptung der Dokumentation zu stehen, dass das letzte Argument nach der Erweiterung berechnet wurde.

Bischof
quelle
1
Woher weiß die Shell, dass Sie E-Mails abrufen?
Rackandboneman
@rackandboneman ohne Bestätigung, ich vermute die internen Überprüfungen basierend aufMAILCHECK
Jeff Schaller
2

Um die Titelfrage zu beantworten, versuchen Sie !$:

$ export EDITOR="emacs -nw"
$ echo !$
EDITOR=emacs -nw

Dies ist eine Erweiterung der Geschichte. Aus der Bash-Manpage:

Die Verlaufserweiterung wird unmittelbar nach dem Lesen einer vollständigen Zeile durchgeführt, bevor die Shell sie in Wörter zerlegt. Es findet in zwei Teilen statt. Die erste besteht darin, zu bestimmen, welche Zeile aus der Verlaufsliste während der Ersetzung verwendet werden soll. Die zweite besteht darin, Teile dieser Zeile für die Aufnahme in die aktuelle auszuwählen. Die aus dem Verlauf ausgewählte Zeile ist das Ereignis, und die Teile dieser Zeile, auf die reagiert wird, sind Wörter.

...

Ereignisbezeichner

...

! Starten Sie eine Verlaufssubstitution, außer wenn ein Leerzeichen, ein Zeilenumbruch, ein Wagenrücklauf, = oder ((wenn die Extglob-Shell-Option mit dem integrierten Shopt aktiviert ist) gefolgt werden.

...

!! Siehe den vorherigen Befehl. Dies ist ein Synonym für "! -1".

...

Wortbezeichner

...

$ Das letzte Wort. Dies ist normalerweise das letzte Argument, wird jedoch auf das nullte Wort erweitert, wenn nur ein Wort in der Zeile enthalten ist.

...

Wenn ein Wortbezeichner ohne Ereignisspezifikation angegeben wird, wird der vorherige Befehl als Ereignis verwendet.

JoL
quelle
Sie nehmen den Titel der Frage waaaaay zu wörtlich. (OK, es ist ein schlechter Titel.) Wir alle können sehen, dass der Befehl « export EDITOR="emacs -nw"» aus zwei Wörtern besteht: das erste ist « export» und das zweite ist « EDITOR="emacs -nw"». Die Frage ist wirklich fragen : „Was die bash man - Seite und die Bash - Handbuch Mittelwert tun , wenn sie sagen , dass !_‚erweitert sich auf die letzte Argument des vorherigen Befehls‘, da die bash - Sets $_zu« EDITOR»in diesem Fall?“ Das Kopieren und Einfügen des Bash-Manpage-Abschnitts zur Verlaufserweiterung ist nicht besonders hilfreich.
G-Man sagt "Reinstate Monica"