Das Ausführen exitin einer Subshell ist eine Falle:
#!/bin/bash
function calc { echo 42; exit 1; }
echo $(calc)
Das Skript druckt 42, beendet die Subshell mit dem Rückkehrcode 1und fährt mit dem Skript fort. Selbst das Ersetzen des Anrufs durch echo $(CALC) || exit 1hilft nicht, da der Rückgabecode echo0 ist, unabhängig vom Rückgabecode von calc. Und calcwird vor ausgeführt echo.
Noch rätselhafter ist es, den Effekt zu vereiteln, exitindem Sie localihn wie im folgenden Skript in Built- in einschließen. Ich bin über das Problem gestolpert, als ich eine Funktion zur Überprüfung eines Eingabewerts geschrieben habe. Beispiel:
Ich möchte eine Datei mit dem Namen "year month day.log" 20141211.logfür heute erstellen . Das Datum wird von einem Benutzer eingegeben, der möglicherweise keinen angemessenen Wert angibt. Daher fnameüberprüfe ich in meiner Funktion den Rückgabewert von date, um die Gültigkeit der Benutzereingabe zu überprüfen:
#!/bin/bash
doit ()
{
local FNAME=$(fname "$1") || exit 1
touch "${FNAME}"
}
fname ()
{
date +"%Y%m%d.log" -d"$1" 2>/dev/null
if [ "$?" != 0 ] ; then
echo "fname reports \"Illegal Date\"" >&2
exit 1
fi
}
doit "$1"
Sieht gut aus. Lassen Sie das Skript benannt werden s.sh. Wenn der Benutzer das Skript mit aufruft ./s.sh "Thu Dec 11 20:45:49 CET 2014", wird die Datei 20141211.logerstellt. Wenn der Benutzer jedoch Folgendes eingibt ./s.sh "Thu hec 11 20:45:49 CET 2014", gibt das Skript Folgendes aus:
fname reports "Illegal Date"
touch: cannot touch ‘’: No such file or directory
Die Zeile fname…besagt, dass in der Subshell fehlerhafte Eingabedaten erkannt wurden. Das exit 1am Ende der local …Zeile wird jedoch nie ausgelöst, da die localDirektive immer zurückkehrt 0. Dies liegt daran local, dass nach ausgeführt wird $(fname)und somit seinen Rückkehrcode überschreibt. Aus diesem Grund wird das Skript fortgesetzt und touchmit einem leeren Parameter aufgerufen. Dieses Beispiel ist einfach, aber das Verhalten von bash kann in einer realen Anwendung ziemlich verwirrend sein. Ich weiß, echte Programmierer benutzen keine Einheimischen. «
Um es deutlich zu machen: Ohne localwird das Skript wie erwartet abgebrochen, wenn ein ungültiges Datum eingegeben wird.
Das Update ist, die Linie wie zu teilen
local FNAME
FNAME=$(fname "$1") || exit 1
Das seltsame Verhalten entspricht der Dokumentation localin der Manpage von bash: "Der Rückgabestatus ist 0, sofern local nicht außerhalb einer Funktion verwendet wird, ein ungültiger Name angegeben wird oder name eine schreibgeschützte Variable ist."
Obwohl dies kein Fehler ist, habe ich das Gefühl, dass das Verhalten von bash nicht intuitiv ist. Mir ist die Ausführungsreihenfolge bekannt, localsollte aber eine fehlerhafte Zuordnung nicht maskieren.
Meine erste Antwort enthielt einige Ungenauigkeiten. Nach einer aufschlussreichen und eingehenden Diskussion mit mikeserv (danke dafür) habe ich sie repariert.