Wann funktioniert inherit_errexit nicht?

Wann funktioniert inherit_errexit nicht?
#!/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 catnicht ausgeführt wurde. Dies wäre der Fall, wenn inherit_errexites deaktiviert wäre.

Das Problem besteht darin, dass set -edie 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 Umleitung
  • false </dev/null→ Status 1 vonfalse
  • a=b→ Status 0, da kein Teil vorhanden ist, das ausfallen kann
  • a=$(exit 0) b=$(exit 1) c=$(exit 2)→ Status 2 der letzten Befehlsersetzung
  • a=$(exit 2) b=$(exit 1) c=$(exit 0)→ Status 0 seit der letzten Befehlsersetzung
  • true $(exit 0) $(exit 1) $(exit 2)→ Status 0 vontrue

Auch hier gilt, set -edass 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 echodas Schreiben fehlschlägt), unabhängig davon, was innerhalb der Befehlsersetzung passiert. Daher wird das Skript hier nicht angehalten, selbst wenn set -eaktiv ist. Ebenso hat im dritten Skript f $(…)der Status 0 von f.

verwandte Informationen