Die genaue Sprache, die in der Single UNIX-Spezifikation zur Beschreibung der Bedeutung von verwendet wird,set -e
lautet:
Wenn diese Option aktiviert ist und ein einfacher Befehl aus einem der in Folgen von Shell-Fehlern aufgeführten Gründe fehlschlägt oder einen Beendigungsstatuswert> 0 zurückgibt und nicht [ein bedingter oder negierter Befehl] ist, wird die Shell sofort beendet.
Es besteht eine Unklarheit darüber, was passiert, wenn ein solcher Befehl in einer Subshell auftritt . Aus praktischer Sicht kann die Subshell nur beendet werden und einen Status ungleich Null an die übergeordnete Shell zurückgeben. Ob die übergeordnete Shell beendet wird, hängt davon ab, ob dieser Nicht-Null-Status zu einem einfachen Befehl führt, der in der übergeordneten Shell fehlschlägt.
Ein solcher problematischer Fall ist der, auf den Sie gestoßen sind: ein Rückgabestatus ungleich Null von einer Befehlsersetzung . Da dieser Status ignoriert wird, wird die übergeordnete Shell nicht beendet. Wie Sie bereits festgestellt haben , können Sie den Exit-Status berücksichtigen, indem Sie die Befehlsersetzung in einer einfachen Zuweisung verwenden : Der Exit-Status der Zuweisung ist der Exit-Status der letzten Befehlsersetzung in der (den) Zuweisung (en) .
Beachten Sie, dass dies nur dann wie vorgesehen ausgeführt wird, wenn es eine einzelne Befehlsersetzung gibt, da nur der Status der letzten Ersetzung berücksichtigt wird. Der folgende Befehl ist beispielsweise erfolgreich (sowohl gemäß dem Standard als auch in jeder Implementierung, die ich gesehen habe):
a=$(false)$(echo foo)
Ein weiterer Fall zu beachten gilt ist ausdrücklich Subshells : (somecommand)
. Gemäß der obigen Interpretation gibt die Subshell möglicherweise einen Status ungleich Null zurück. Da dies jedoch kein einfacher Befehl in der übergeordneten Shell ist, sollte die übergeordnete Shell fortgesetzt werden. Tatsächlich lassen alle mir bekannten Muscheln die Eltern zu diesem Zeitpunkt zurückkehren. Dies ist in vielen Fällen hilfreich, z. B. (cd /some/dir && somecommand)
wenn die Klammern verwendet werden, um einen Vorgang wie einen aktuellen Verzeichniswechsel lokal zu halten. Es verstößt jedoch gegen die Spezifikation, wenn set -e
in der Subshell deaktiviert ist oder wenn die Subshell auf eine Weise einen Nicht-Null-Status zurückgibt würde es nicht beenden, z. B. mit !
einem wahren Befehl. Zum Beispiel werden alle Befehle ash, bash, pdksh, ksh93 und zsh ohne Anzeige foo
in den folgenden Beispielen beendet:
set -e; (set +e; false); echo "This should be displayed"
set -e; (! true); echo "This should be displayed"
Doch kein einfacher Befehl ist fehlgeschlagen, solange er set -e
in Kraft war!
Ein dritter problematischer Fall sind Elemente in einer nichttrivialen Pipeline . In der Praxis ignorieren alle Shells Ausfälle der Elemente der Pipeline mit Ausnahme des letzten und zeigen in Bezug auf das letzte Pipelineelement eines von zwei Verhaltensweisen:
- ATT ksh und zsh, die das letzte Element der Pipeline in der übergeordneten Shell ausführen, verhalten sich wie gewohnt: Wenn ein einfacher Befehl im letzten Element der Pipeline fehlschlägt, führt die Shell diesen Befehl aus, der zufällig die übergeordnete Shell ist. Ausgänge.
- Andere Shells approximieren das Verhalten, indem sie beendet werden, wenn das letzte Element der Pipeline einen Status ungleich Null zurückgibt.
Wie zuvor set -e
bewirkt das Deaktivieren oder Verwenden einer Negation im letzten Element der Pipeline, dass der Status ungleich Null zurückgegeben wird, sodass die Shell nicht beendet wird. Andere Shells als ATT ksh und zsh werden dann beendet.
Die pipefail
Option von Bash bewirkt, dass eine Pipeline sofort beendet wird, set -e
wenn eines ihrer Elemente einen Nicht-Null-Status zurückgibt.
Beachten Sie, dass Bash als weitere Komplikation set -e
in Subshells deaktiviert wird, es sei denn, es befindet sich im POSIX-Modus ( set -o posix
oder POSIXLY_CORRECT
in der Umgebung, wenn Bash gestartet wird ).
All dies zeigt, dass die POSIX-Spezifikation bei der Angabe der -e
Option leider einen schlechten Job macht . Glücklicherweise sind die vorhandenen Muscheln in ihrem Verhalten größtenteils konsistent.
set -e; (cd /nonexisting)
.(Ich beantworte meine Frage, weil ich eine Lösung gefunden habe.) Eine Lösung besteht darin, diese immer einer Zwischenvariablen zuzuweisen. Auf diese Weise wird der Returncode (
$?
) gesetzt.Damit
Wird jedoch ausgegeben
1
(oder stattdessen beendet, wennset -e
vorhanden):Wird
0
nach einer Leerzeile ausgegeben . Der Rückkehrcode des Echos (oder eines anderen Befehls, der mit der Backtick-Ausgabe ausgeführt wurde) ersetzt den Rückkehrcode 0.Ich bin immer noch offen für Lösungen, für die keine Zwischenvariable erforderlich ist, aber das hilft mir ein bisschen.
quelle
Wie das OP in seiner eigenen Antwort ausgeführt hat, löst das Zuweisen der Ausgabe des Unterbefehls zu einer Variablen das Problem. das
$?
bleibt unversehrt.Ein Randfall kann Sie jedoch immer noch mit falschen Negativen verwirren (dh der Befehl schlägt fehl, aber der Fehler tritt nicht auf),
local
Variablendeklaration:local myvar=$(subcommand)
Wir werden immer wiederkommen0
!bash(1)
weist darauf hin:Hier ist ein einfacher Testfall:
Die Ausgabe:
quelle
VAR=...
undlocal VAR=...
hat mich so richtig durcheinander gebracht!Wie andere gesagt haben,
local
wird immer 0 zurückgegeben. Die Lösung besteht darin, die Variable zuerst zu deklarieren:Ausgabe:
quelle
Um bei einem Fehler
-e
bei der Befehlsersetzung zu beenden, können Sie Folgendes explizit in einer Subshell festlegen:quelle
Interessanter Punkt!
Ich bin noch nie darauf gestoßen, weil ich kein Freund von
set -e
(stattdessen ziehe ich es vortrap ... ERR
) bin, aber das bereits getestet habe:trap ... ERR
Fang auch keine Fehler in$(...)
(oder den altmodischen Backticks).Ich denke das Problem ist (wie so oft), dass hier eine Subshell aufgerufen wird und
-e
explizit die aktuelle Shell bedeutet.Die einzige andere Lösung, die in diesem Moment in den Sinn kam, wäre read:
Dies wirft
ERR
und mit-e
der Shell wird beendet. Einziges Problem: Dies funktioniert nur für Befehle mit einer Ausgabezeile (oder Sie leiten durch etwas, das Zeilen verbindet).quelle