Ich habe in letzter Zeit einige Skripte wie diese gefunden:
( set -e ; do-stuff; do-more-stuff; ) || echo failed
Das sieht für mich gut aus, aber es funktioniert nicht! Das set -e
gilt nicht, wenn Sie das hinzufügen ||
. Ohne das funktioniert es gut:
$ ( set -e; false; echo passed; ); echo $?
1
Wenn ich jedoch das hinzufüge ||
, set -e
wird das ignoriert:
$ ( set -e; false; echo passed; ) || echo failed
passed
Die Verwendung einer echten, separaten Shell funktioniert wie erwartet:
$ sh -c 'set -e; false; echo passed;' || echo failed
failed
Ich habe dies in mehreren verschiedenen Shells versucht (bash, dash, ksh93) und alle verhalten sich gleich, es ist also kein Fehler. Kann das jemand erklären?
shell
shell-script
Verrückter Wissenschaftler
quelle
quelle
||
Außenseite der Subshell beeinflusst das Verhalten innerhalb der Subshell.(set -e; echo 1; false; echo 2)
mit(set -e; echo 1; false; echo 2) || echo 3
Antworten:
Nach diesem Thread , dann ist es das Verhalten POSIX legt fest , für die Verwendung von „
set -e
“ in einer Subshell.(Ich war auch überrascht.)
Erstens das Verhalten:
Der zweite Beitrag stellt fest,
Es gibt ein wenig mehr im vierten Beitrag, auch von Eric Blake,
Dieses Verhalten ist auf jeden Fall überraschend. Es ist kontraintuitiv: Man würde erwarten, dass die erneute Aktivierung von
set -e
eine Wirkung hat und dass der umgebende Kontext keinen Präzedenzfall darstellt; Darüber hinaus macht der Wortlaut des POSIX-Standards dies nicht besonders deutlich. Wenn Sie es in dem Kontext lesen, in dem der Befehl fehlschlägt, gilt die Regel nicht: Es gilt nur im umgebenden Kontext, es gilt jedoch vollständig für ihn.quelle
set -e; (false; echo passed;) || echo failed
. Es überrascht mich eigentlich nicht, dass -e in diesem Fall angesichts des Wortlauts der Norm ignoriert wird. In meinem Fall setze ich jedoch explizit -e in die Subshell und erwarte, dass die Subshell bei einem Fehler beendet wird. Es gibt keine UND-ODER-Liste in der Subshell ...if
und||
und&&
sind ansteckend? Das ist absurdset -e
Hat in der Tat keine Auswirkung in Subshells, wenn Sie den||
Operator danach verwenden. zB würde das nicht funktionieren:Aaron D. Marasco erklärt in seiner Antwort hervorragend, warum es sich so verhält.
Hier ist ein kleiner Trick, mit dem dies behoben werden kann: Führen Sie den inneren Befehl im Hintergrund aus und warten Sie sofort darauf. Der
wait
eingebaute Code gibt den Exit-Code des inneren Befehls zurück. Jetzt verwenden Sie||
afterwait
, nicht die innere Funktion, undset -e
funktioniert in letzterer ordnungsgemäß:Hier ist die generische Funktion, die auf dieser Idee aufbaut. Es sollte in allen POSIX-kompatiblen Shells funktionieren, wenn Sie
local
Schlüsselwörter entfernen , dh allelocal x=y
durch Folgendes ersetzenx=y
:Anwendungsbeispiel:
Beispiel ausführen:
Das einzige, was Sie beachten müssen, wenn Sie diese Methode verwenden, ist, dass alle Änderungen an Shell-Variablen, die Sie mit dem übergebenen Befehl vornehmen,
run
nicht an die aufrufende Funktion weitergegeben werden, da der Befehl in einer Subshell ausgeführt wird.quelle
Ich würde nicht ausschließen, dass es ein Fehler ist, nur weil sich mehrere Shells so verhalten. ;-)
Ich habe mehr Spaß zu bieten:
Darf ich aus Man Bash zitieren (4.2.24):
Vielleicht führt die Auswertung mehrerer Befehle dazu, dass das || ignoriert wird Kontext.
quelle
eval
Trick funktioniert bei mir nicht. Ich habe versucht, Bash, Bash im Posix-Modus und Dash.set -e
.set -e
ist absichtlich kaputt. Ich würde es nur für das einfachste Shellskript ohne Kontrollstrukturen, Subshells oder Befehlsersetzungen verwenden.Problemumgehung, wenn Sie die oberste Ebene verwenden
set -e
Ich bin auf diese Frage gekommen, weil ich
set -e
als Fehlererkennungsmethode Folgendes verwendet habe:und ohne
||
würde das Skript aufhören zu laufen und niemals erreichendo_more_stuff
.Da es keine saubere Lösung zu geben scheint, denke ich, dass ich
set +e
meine Skripte einfach bearbeiten werde:quelle