Es ist (für POSIX) in Abschnitt 2.9.1 Einfache Befehle
der Open Group Base-Spezifikationen dokumentiert . Dort gibt es eine Textwand; Ich lenke Ihre Aufmerksamkeit auf den letzten Absatz:
Wenn ein Befehlsname ist, wird die Ausführung wird wie beschrieben weiter in Befehl Suchen und Ausführung . Wenn kein Befehlsname vorhanden ist, der Befehl jedoch eine Befehlsersetzung enthielt, muss der Befehl mit dem Beendigungsstatus der zuletzt durchgeführten Befehlsersetzung abgeschlossen werden. Andernfalls wird der Befehl mit einem Null-Exit-Status abgeschlossen.
So zum Beispiel
Command Exit Status
$ FOO=BAR 0 (but see also the note from icarus, below)
$ FOO=$(bar) Exit status from "bar"
$ FOO=$(bar) baz Exit status from "baz"
$ foo $(bar) Exit status from "foo"
So funktioniert Bash auch. Siehe aber auch den Abschnitt „nicht so einfach“ am Ende.
phk , in seiner frage Zuweisungen sind wie Befehle mit einem Exit-Status, außer wenn es eine Befehlsersetzung gibt? , schlägt vor
… Es sieht so aus, als ob eine Zuweisung selbst als Befehl zählt… mit einem Exit-Wert von Null, der jedoch vor der rechten Seite der Zuweisung gilt (z. B. ein Befehlsersetzungsaufruf…)
Das ist keine schreckliche Sichtweise. Ein grobes Schema des Rückgabestatus eines einfachen Befehl zur Bestimmung (ein nicht enthält ;
, &
, |
, &&
oder ||
) ist:
- Scannen Sie die Zeile von links nach rechts, bis Sie das Ende oder ein Befehlswort (normalerweise einen Programmnamen) erreichen.
- Wenn Sie eine Variablenzuweisung sehen, ist der Rückgabestatus für die Zeile möglicherweise 0.
- Wenn Sie eine Befehlsersetzung sehen, dh
$(…)
den Exit-Status dieses Befehls übernehmen.
- Wenn Sie einen tatsächlichen Befehl erreichen (nicht in einer Befehlsersetzung), übernehmen Sie den Exit-Status dieses Befehls.
Der Rückgabestatus für die Zeile ist die letzte Nummer, auf die Sie gestoßen sind.
Befehlsersetzungen als Argumente an den Befehl, zum Beispiel foo $(bar)
, nicht zählen; Sie erhalten den Exit-Status von foo
. Um die Notation von phk zu paraphrasieren , ist das Verhalten hier
temporary_variable = EXECUTE( "bar" )
overall_exit_status = EXECUTE( "foo", temporary_variable )
Dies ist jedoch eine leichte Vereinfachung. Der allgemeine Rückgabestatus von
A = $ ( cmd 1 ) B = $ ( cmd 2 ) C = $ ( cmd 3 ) D = $ ( cmd 4 ) E = mc 2
ist der Exit-Status von . Die Zuweisung, die nach der Zuweisung erfolgt, setzt den Gesamt-Exit-Status nicht auf 0.
cmd4
E=
D=
icarus wirft in seiner antwort auf die frage von phk einen wichtigen punkt auf: variablen können als schreibgeschützt gesetzt werden. Der vorletzte Absatz in Abschnitt 2.9.1 des POSIX-Standards lautet:
Wenn eine der Variablenzuweisungen versucht, einer Variablen, für die das schreibgeschützte Attribut in der aktuellen Shell-Umgebung festgelegt ist , einen Wert zuzuweisen (unabhängig davon, ob die Zuweisung in dieser Umgebung erfolgt), tritt ein Variablenzuweisungsfehler auf. Die Konsequenzen dieser Fehler finden Sie unter Folgen von Shell- Fehlern.
Also, wenn du sagst
readonly A
C=Garfield A=Felix T=Tigger
der Rückgabestatus ist 1. Es spielt keine Rolle , ob die Saiten Garfield
, Felix
und / oder Tigger
mit Kommandosubstitution (en) ersetzt - aber Hinweise weiter unten sehen.
Abschnitt 2.8.1 Folgen von Shell-Fehlern enthält einen weiteren Textbündel und eine Tabelle und endet mit
In allen in der Tabelle aufgeführten Fällen, in denen eine interaktive Shell nicht beendet werden muss, darf die Shell den Befehl, in dem der Fehler aufgetreten ist, nicht weiter verarbeiten.
Einige Details sind sinnvoll; manche nicht:
- Die
A=
Zuweisung bricht manchmal die Befehlszeile ab, wie der letzte Satz zu spezifizieren scheint. Im obigen Beispiel C
ist auf gesetzt Garfield
, aber T
nicht gesetzt (und natürlich auch nicht A
).
- Ebenso wird
ausgeführt,
aber nicht . Aber in meinen bash - Versionen (die 4.1.X und 4.3.X enthalten), ist es nicht ausführen . (Dies wirkt sich übrigens weiter auf die Interpretation von phk aus, dass der Exit-Wert der Zuweisung vor der rechten Seite der Zuweisung gilt.)
C=$(cmd1) A=$(cmd2) T=$(cmd3)
cmd1
cmd3
cmd2
Aber hier ist eine Überraschung:
In meinen Versionen von Bash,
schreibgeschützt A.
C = etwas A = etwas T = etwas cmd 0
wird ausgeführt. Im Speziellen,cmd0
C = $ ( cmd 1 ) A = $ ( cmd 2 ) T = $ ( cmd 3 ) cmd 0
führt aus
und , aber nicht . (Beachten Sie, dass dies das Gegenteil von seinem Verhalten ist, wenn kein Befehl vorhanden ist.) Und es setzt (sowie ) in der Umgebung von . Ich frage mich, ob dies ein Fehler in Bash ist.
cmd1
cmd3
cmd2
T
C
cmd0
Nicht so einfach:
Der erste Absatz dieser Antwort bezieht sich auf „einfache Befehle“.
Die Spezifikation sagt,
Ein „einfacher Befehl“ ist eine Folge von optionalen Variablenzuweisungen und -umleitungen in beliebiger Reihenfolge, optional gefolgt von Wörtern und Umleitungen, die von einem Steuerungsoperator beendet werden.
Dies sind Anweisungen wie in meinem ersten Beispielblock:
$ FOO=BAR
$ FOO=$(bar)
$ FOO=$(bar) baz
$ foo $(bar)
Die ersten drei enthalten Variablenzuweisungen, und die letzten drei enthalten Befehlsersetzungen.
Einige Variablenzuweisungen sind jedoch nicht ganz so einfach.
Bash (1) sagt:
Zuweisungsanweisungen können auch als Argumente an die erscheinen alias
, declare
, typeset
, export
, readonly
, und local
builtin Befehle ( Erklärung Befehle).
Für export
, die POSIX - Spezifikation sagt,
STATUS BEENDEN
0Alle Namensoperanden wurden erfolgreich exportiert.
> 0Mindestens ein Name konnte nicht exportiert werden oder die -p
Option wurde angegeben und ein Fehler ist aufgetreten.
Und POSIX unterstützt nicht local
, aber bash (1) sagt:
Es ist ein Fehler, local
wenn Sie sich nicht in einer Funktion befinden. Der Rückgabestatus ist 0, es local
sei denn, er wird außerhalb einer Funktion verwendet, ein ungültiger Name wird angegeben oder name ist eine schreibgeschützte Variable.
Wenn wir zwischen den Zeilen lesen, können wir sehen, dass Deklarationsbefehle wie
export FOO=$(bar)
und
local FOO=$(bar)
sind eher wie
foo $(bar)
soweit sie den Exit - Status von ignorieren bar
und gibt Ihnen einen Exit - Status auf Basis des Hauptbefehl ( export
, local
oder foo
). Also haben wir Verrücktheit wie
Command Exit Status
$ FOO=$(bar) Exit status from "bar"
(unless FOO is readonly)
$ export FOO=$(bar) 0 (unless FOO is readonly,
or other error from “export”)
$ local FOO=$(bar) 0 (unless FOO is readonly,
statement is not in a function,
or other error from “local”)
mit denen wir demonstrieren können
$ export FRIDAY=$(date -d tomorrow)
$ echo "FRIDAY = $FRIDAY, status = $?"
FRIDAY = Fri, May 04, 2018 8:58:30 PM, status = 0
$ export SATURDAY=$(date -d "day after tomorrow")
date: invalid date ‘day after tomorrow’
$ echo "SATURDAY = $SATURDAY, status = $?"
SATURDAY = , status = 0
und
myfunc() {
local x=$(echo "Foo"; true); echo "x = $x -> $?"
local y=$(echo "Bar"; false); echo "y = $y -> $?"
echo -n "BUT! "
local z; z=$(echo "Baz"; false); echo "z = $z -> $?"
}
$ myfunc
x = Foo -> 0
y = Bar -> 0
BUT! z = Baz -> 1
Glücklicherweise fängt ShellCheck den Fehler ab und löst SC2155 aus , was darauf hinweist
export foo="$(mycmd)"
sollte geändert werden zu
foo=$(mycmd)
export foo
und
local foo="$(mycmd)"
sollte geändert werden zu
local foo
foo=$(mycmd)
local
auswirken? ZBlocal foo=$(bar)
?