"set -e" beendet das Skript nicht, wenn ein Fehler in der Bedingung auftritt

"set -e" beendet das Skript nicht, wenn ein Fehler in der Bedingung auftritt

Das folgende Skript weist einen Syntaxfehler oder einen anderen Fehler auf:

#!/usr/bin/env bash
set -euo pipefail

if [ ! -f /custom.log]; then
  echo "test"
fi
abcxyz

Das Skript schlägt mit der folgenden Ausgabe fehl:

./test.sh: line 4: [: missing `]'
./test.sh: line 7: abcxyz: command not found

Ich bin nicht daran interessiert, wie ich dieses Skript behebe, sondern wie ich verhindern kann, dass das Skript weiter ausgeführt wird, wenn dieser Fehler auftritt? Ich hätte gedacht, dass set -edieses Verhalten erzwungen wird.

Antwort1

set -ewird nicht bei fehlgeschlagenen Befehlen ausgelöst, die als Bedingungen verwendet werden, wie im Bedingungsabschnitt von if/ while/ until-Konstrukten oder links von einem ||, &&oder in Funktionen, Subshells, Quelldateien evalusw., die unter diesen Bedingungen aufgerufen werden.

Wenn ja, dann:

if [ ! -f /custom.log ]; then

Würde das Skript beenden, wenn es /custom.logsich um eine reguläre Datei handeln würde, da es [dann auch mit einem Exit-Status ungleich Null beendet würde.

Der [integrierte Befehl der bashShell (und der meisten anderen Implementierungen) wird mit einem 1Status beendet, wenn die getestete Bedingung nicht erfüllt ist und 2ein Syntaxfehler vorliegt (allerdings nicht alle Syntaxfehler, beispielsweise nicht in [ -v 'a[+]' ]).POSIX erfordert, dass der Exit-Status im Fehlerfall größer als 1 ist.

Sie können das Skript also beenden, wenn ein Befehl mit einem Code größer als 1 beendet wird, unabhängig davon, ob er in einer Bedingung verwendet wird oder nicht, mit etwas wie:

shopt -s extdebug # make sure the DEBUG trap propagates to subshells
trap '(($?>1 && (ret=$?))) && exit "$ret"' DEBUG
[ -f / ] || echo / not a regular file # OK
[ -f /] || echo was a syntax error # causes an exit, not output
echo not reached

ERRBeachten Sie, dass Sie die Falle hierfür nicht verwenden können, da die ERRFalle nur unter denselben Bedingungen ausgeführt wird wie diejenigen, die den Ausgang auslösen set -e.

Beachten Sie jedoch die Konsequenzen. Dies würde beispielsweise Folgendes zur Folge haben:

if grep -qs pattern /file; then
  echo pattern was found in /file
fi

zu beenden, wenn /filees nicht existierte oder nicht lesbar war, da grepin diesem Fall der Status 2 zurückgegeben wird, obwohl bei -sklar die Absicht bestand, diese Fälle zu ignorieren.

Sie müssen also darauf achten, unter welchen Bedingungen die in Ihren Bedingungen verwendeten Befehle mit einem Status größer als 1 beendet werden können. Um diese zu umgehen, benötigen Sie etwas wie:

if sh -c 'grep -sq pattern / file || exit 1'; then...

Sie könnten einschränken dieBeenden bei Beendigungsstatus größer als 1zum [oder testBefehl mit etwas wie:

unset -v previous_BASH_COMMAND
trap '
  case $previous_BASH_COMMAND in
    ("[ "* | "test "*) (($?>1 && (ret=$?))) && exit "$ret"
  esac
  previous_BASH_COMMAND=$BASH_COMMAND' DEBUG

Das hat einige Einschränkungen.

echo x
([ -f/]; echo y)

Dies würde dazu führen, dass die Subshell beendet wird, nicht jedoch die übergeordnete Shell, da $previous_BASH_COMMANDdort kein Wert festgelegt wurde. Und in:

[ -f / ] && echo a regular file
(grep -qs foo /file && echo foo in /file)
echo here

Die Shell würde beim Ausführen beendet echo here, da $? 2 wäre und $previous_BASH_COMMANDwar [ -f / ].

Auf jeden Fall Dinge wie

[ -f /] | cat
export var="$([ -f /])"

konnte nicht erkannt werden, da der Beendigungsstatus nicht an den übergeordneten Shell-Prozess weitergegeben wird (außer mit der pipefailOption im ersten Fall).

Nun bin ich nicht sicher, ob es den Aufwand wert ist, diese Art der (instabilen) Erkennung zur Laufzeit hinzuzufügen, wenn der Fehler zur Entwicklungszeit (wenn Sie Ihr Skript schreiben und testen) leicht erkennbar ist.

verwandte Informationen