Zuweisungen sind wie Befehle mit einem Exit-Status, außer wenn Befehle ersetzt werden.


10

Siehe die folgenden Beispiele und ihre Ausgaben in POSIX-Shells:

  1. false;echo $?oder false || echo 1:1
  2. false;foo="bar";echo $?oder foo="bar" && echo 0:0
  3. foo=$(false);echo $?oder foo=$(false) || echo 1:1
  4. foo=$(true);echo $?oder foo=$(true) && echo 0:0

Wie in der am höchsten bewerteten Antwort unter /programming/6834487/what-is-the-variable-in-shell-scripting erwähnt :

$? wird verwendet, um den Rückgabewert des zuletzt ausgeführten Befehls zu ermitteln.

Dies ist in diesem Fall wahrscheinlich etwas irreführend. Lassen Sie uns also die POSIX-Definition erhalten, die auch in einem Beitrag aus diesem Thread zitiert wird:

? Wird auf den dezimalen Exit-Status der letzten Pipeline erweitert (siehe Pipelines).

Es sieht also so aus, als ob eine Zuweisung selbst als Befehl (oder vielmehr als Pipeline-Teil) mit einem Exit-Wert von Null zählt, der jedoch vor der rechten Seite der Zuweisung gilt (z. B. die Befehlsersetzungsaufrufe in meinen Beispielen hier).

Ich sehe, wie dieses Verhalten aus praktischer Sicht sinnvoll ist, aber es scheint mir etwas ungewöhnlich, dass die Aufgabe selbst in dieser Reihenfolge zählt. Um klarer zu machen, warum es mir fremd ist, nehmen wir an, dass die Aufgabe eine Funktion war:

ASSIGNMENT( VARIABLE, VALUE )

dann foo="bar"wäre es

ASSIGNMENT( "foo", "bar" )

und foo=$(false)wäre so etwas wie

ASSIGNMENT( "foo", EXECUTE( "false" ) )

was bedeuten würde , dass die EXECUTELäufe zuerst und erst danach ASSIGNMENT ausgeführt wird , aber es ist immer noch der EXECUTEStatus, der hier ankommt.

Bin ich in meiner Einschätzung richtig oder missverstehe / vermisse ich etwas? Sind das die richtigen Gründe, warum ich dieses Verhalten als "seltsam" betrachte?


1
Entschuldigung, aber mir ist unklar, was Sie seltsam finden.
Kusalananda

1
@Kusalananda Vielleicht hilft es Ihnen zu sagen, dass es damit begann, dass ich mich fragte: "Warum wird false;foo="bar";echo $?immer 0 zurückgegeben, wenn der letzte echte Befehl ausgeführt wurde false?" Grundsätzlich verhalten sich Zuweisungen besonders, wenn es um Exit-Codes geht. Ihr Exit-Code ist immer 0, außer wenn dies nicht auf etwas zurückzuführen ist, das als Teil der rechten Seite der Zuweisung ausgeführt wurde.
Phk

Antworten:


10

Der Exit-Status für Zuweisungen ist seltsam . Die offensichtlichste Möglichkeit, dass eine Zuweisung fehlschlägt, besteht darin, dass die Zielvariable markiert ist readonly.

$ err(){ echo error ; return ${1:-1} ; }
$ PS1='$? $ '
0 $ err 42
error
42 $ A=$(err 12)
12 $ if A=$(err 9) ; then echo wrong ; else E=$? ; echo "E=$E ?=$?" ; fi
E=9 ?=0
0 $ readonly A
0 $ if A=$(err 10) ; then echo wrong ; else E=$? ; echo "E=$E ?=$?" ; fi
A: is read only
1 $

Beachten Sie, dass weder der wahre noch der falsche Pfad der if-Anweisung verwendet wurden. Die fehlgeschlagene Zuweisung hat die Ausführung der gesamten Anweisung gestoppt. bash im POSIX-Modus und ksh93 und zsh brechen alle ein Skript ab, wenn eine Zuweisung fehlschlägt.

Um den POSIX-Standard dazu zu zitieren :

Ein Befehl ohne Befehlsnamen, der jedoch eine Befehlsersetzung enthält, hat den Exit-Status der letzten Befehlsersetzung, die die Shell ausgeführt hat.

Dies ist genau der Teil der Shell-Grammatik

 foo=$(err 42)

simple_commandDies kommt von einem (simple_command → cmd_prefix → ASSIGNMENT_WORD). Wenn eine Zuweisung erfolgreich ist, ist der Exit-Status Null, es sei denn, es handelt sich um eine Befehlsersetzung. In diesem Fall ist der Exit-Status der Status des letzten. Wenn die Zuweisung fehlschlägt, ist der Exit-Status ungleich Null, aber Sie können ihn möglicherweise nicht abfangen.


1
Um Ihre Antwort zu ergänzen, hier ist eine Antwort aus einem anderen Thread, in dem ein neuerer POSIX-Standard dazu zitiert wird. Die Schlussfolgerung ist im Grunde dieselbe: unix.stackexchange.com/a/270831/117599
phk

4

Du sagst,

… 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. Aber es ist 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.cmd4E=D=

Auch, wie Ikarus weist darauf hin , können Variablen als nur lesbar gesetzt werden. Betrachten Sie die folgende Variation des Beispiels von icarus:

$ err() { echo "stdout $*"; echo "stderr $*" >&2; return ${1:-1}; }
$ readonly A
$ Z=$(err 41 zebra) A=$(err 42 antelope) B=$(err 43 badger)
stderr 41 zebra
stderr 42 antelope
bash: A: readonly variable
$ echo $?
1
$ printf "%s = %s\n" Z "$Z" A "$A" B "$B"
Z = stdout 41 zebra
A =
B =
$

Obwohl Anur lesbar ist, führt bash den Befehl Substitution rechts von A=- und dann bricht den Befehl , da Anur lesbar ist. Dies widerspricht weiter Ihrer Interpretation, dass der Exit-Wert der Zuordnung vor der rechten Seite der Zuweisung gilt.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.