
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
a=$(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
echo -n $(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
survived
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
f() { :; }
f $(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
survived
Gibt es noch andere Fälle? Oder eine Verallgemeinerung?
Antwort1
TL,DR: Um davon zu profitieren set -e
, weisen Sie das Ergebnis einer Befehlsersetzung direkt einer Variablen zu (optional mit zusätzlichen Zeichenfolgen darum herum). Kombinieren Sie nicht mehrere Befehlsersetzungen miteinander und verwenden Sie keine Befehlsersetzung in einem Befehlsargument.
Das Problem liegt nicht bei inherit_errexit
. Es funktioniert. Das Problem sind die Einschränkungen von set -e
(die nicht spezifisch für Bash sind: andere sh-ähnliche Shells haben das gleiche Problem).
Demonstration: Führen Sie diese Variante Ihres zweiten Beispiels aus.
$ cat b2.sh
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
echo -n $(cat no-such-file; echo >&2 after cat)
echo survived
$ ./b2.sh
cat: no-such-file: No such file or directory
survived
Beachten Sie, dass dies echo >&2 after cat
nicht ausgeführt wurde. Dies wäre der Fall, wenn inherit_errexit
es deaktiviert wäre.
Das Problem besteht darin, dass set -e
die Ausführung nur in einfachen Fällen bei einem Fehler gestoppt wird. Wenn eine Befehlsersetzung einen Fehlerstatus zurückgibt, wird dadurch die Ausführung des einfachen Befehls, der die Ersetzung enthält, nicht gestoppt. Es kann höchstens den Rückgabestatus des einfachen Befehls festlegen, was wiederum die Ausführung des Skripts stoppen kann. Ein „einfacher Befehl“ besteht aus Zuweisungen, Umleitungen und einem optionalen ausführbaren Befehlsnamen und Argumenten. Wenn eine Umleitung fehlschlägt, ist der Rückgabestatus 1. Andernfalls, wenn ein Befehlsname vorhanden ist, ist der Rückgabestatus des einfachen Befehls der Rückgabestatus des ausführbaren Befehls. Andernfalls ist der Rückgabestatus der Rückgabestatus der letzten Befehlsersetzung oder 0, wenn es keine gibt. Hier sind einige Beispiele für einfache Befehle:
true </no/such/file
→ Status 1 aufgrund der fehlgeschlagenen Umleitungfalse </dev/null
→ Status 1 vonfalse
a=b
→ Status 0, da kein Teil vorhanden ist, das ausfallen kanna=$(exit 0) b=$(exit 1) c=$(exit 2)
→ Status 2 der letzten Befehlsersetzunga=$(exit 2) b=$(exit 1) c=$(exit 0)
→ Status 0 seit der letzten Befehlsersetzungtrue $(exit 0) $(exit 1) $(exit 2)
→ Status 0 vontrue
Auch hier gilt, set -e
dass das Skript in allen Fällen nur dann angehalten wird, wenn der Status des Befehls ungleich 0 ist. Der Status eingebetteter Befehle ist nicht direkt relevant.
Und so hat in Ihrem zweiten Skript echo -n $(…)
der Status 0, von echo
(außer wenn echo
das Schreiben fehlschlägt), unabhängig davon, was innerhalb der Befehlsersetzung passiert. Daher wird das Skript hier nicht angehalten, selbst wenn set -e
aktiv ist. Ebenso hat im dritten Skript f $(…)
der Status 0 von f
.