Es gibt noch ein paar weitere Möglichkeiten, wie Sie dieses Problem angehen können. Angenommen, eine Ihrer Anforderungen besteht darin, ein Shell-Skript / eine Shell-Funktion auszuführen, die einige Shell-Befehle enthält, und zu überprüfen, ob das Skript erfolgreich ausgeführt wurde, und bei Fehlern Fehler auszugeben.
Die Shell-Befehle verlassen sich im Allgemeinen auf zurückgegebene Exit-Codes, um die Shell darüber zu informieren, ob sie aufgrund einiger unerwarteter Ereignisse erfolgreich war oder fehlgeschlagen ist.
Was Sie also tun möchten, fällt in diese beiden Kategorien
- bei Fehler beenden
- Beenden und Aufräumen bei Fehler
Je nachdem, was Sie tun möchten, stehen Ihnen Shell-Optionen zur Verfügung. Für den ersten Fall bietet die Shell eine Option mit set -e
und für die zweite könnten Sie eine trap
machen am EXIT
Soll ich exit
verwenden in meinem Skript/meiner Funktion?
Mit exit
verbessert im Allgemeinen die Lesbarkeit In bestimmten Routinen möchten Sie sofort zur aufrufenden Routine zurückkehren, sobald Sie die Antwort kennen. Wenn die Routine so definiert ist, dass sie keine weitere Bereinigung erfordert, sobald sie einen Fehler erkennt, bedeutet ein nicht sofortiges Beenden, dass Sie mehr Code schreiben müssen.
Wenn Sie also Bereinigungsaktionen im Skript durchführen müssen, um die Beendigung des Skripts sauber zu machen, ist dies nicht vorzuziehen um exit
zu verwenden .
Soll ich set -e
verwenden für Fehler beim Beenden?
Nein!
set -e
war ein Versuch, der Shell eine "automatische Fehlererkennung" hinzuzufügen. Sein Ziel war es, die Shell zu veranlassen, jedes Mal abzubrechen, wenn ein Fehler auftritt, aber es birgt viele potenzielle Fallstricke, zum Beispiel
-
Die Befehle, die Teil eines if-Tests sind, sind immun. Im Beispiel, wenn Sie erwarten, dass es bei
test
unterbrochen wird Überprüfen Sie das nicht vorhandene Verzeichnis, würde dies nicht der Fall sein, wird es zur Else-Bedingung weitergeleitetset -e f() { test -d nosuchdir && echo no dir; } f echo survived
-
Befehle in einer anderen Pipeline als der letzten sind immun. Im Beispiel unten wird der Exit-Code des zuletzt ausgeführten (ganz rechts) Befehls berücksichtigt (
cat
) und es war erfolgreich. Dies könnte durch Setzen desset -o pipefail
vermieden werden Option, aber es ist immer noch eine Einschränkung.set -e somecommand that fails | cat - echo survived
Empfohlen für die Verwendung - trap
beim Beenden
Das Urteil lautet, wenn Sie in der Lage sein möchten, einen Fehler zu behandeln, anstatt blind zu beenden, anstatt set -e
zu verwenden , verwenden Sie einen trap
auf der ERR
Pseudosignal.
Die ERR
trap soll keinen Code ausführen, wenn die Shell selbst mit einem Fehlercode ungleich Null beendet wird, sondern wenn irgendein Befehl von dieser Shell ausgeführt wird, der nicht Teil einer Bedingung ist (wie in if cmd
, oder cmd ||
) wird mit einem Exit-Status ungleich Null beendet.
Die allgemeine Praxis ist, dass wir einen Trap-Handler definieren, um zusätzliche Debug-Informationen darüber bereitzustellen, welche Zeile und was das Beenden verursacht. Merken Sie sich den Beendigungscode des letzten Befehls, der den ERR
verursacht hat Signal wäre zu diesem Zeitpunkt noch verfügbar.
cleanup() {
exitcode=$?
printf 'error condition hit\n' 1>&2
printf 'exit code returned: %s\n' "$exitcode"
printf 'the command executing at the time of the error was: %s\n' "$BASH_COMMAND"
printf 'command present on line: %d' "${BASH_LINENO[0]}"
# Some more clean up code can be added here before exiting
exit $exitcode
}
und wir verwenden einfach diesen Handler wie unten auf dem Skript, das fehlschlägt
trap cleanup ERR
Setzen Sie dies in einem einfachen Skript zusammen, das false
enthielt in Zeile 15 die Informationen, die Sie erhalten würden als
error condition hit
exit code returned: 1
the command executing at the time of the error was: false
command present on line: 15
Die trap
bietet unabhängig vom Fehler auch Optionen, um die Bereinigung einfach nach Abschluss der Shell auszuführen (z. B. wenn Ihr Shell-Skript beendet wird), bei Signal EXIT
. Sie können auch mehrere Signale gleichzeitig abfangen. Die Liste der unterstützten Signale zum Trap finden Sie auf der Handbuchseite trap.1p - Linux
Beachten Sie auch, dass keine der bereitgestellten Methoden funktioniert, wenn Sie mit Sub-Shells zu tun haben. In diesem Fall müssen Sie möglicherweise Ihre eigene Fehlerbehandlung hinzufügen.
-
Auf einer Sub-Shell mit
set -e
würde nicht funktionieren. Diefalse
ist auf die Sub-Shell beschränkt und wird nie an die übergeordnete Shell weitergegeben. Um hier die Fehlerbehandlung durchzuführen, fügen Sie Ihre eigene Logik hinzu, um(false) || false
auszuführenset -e (false) echo survived
-
Dasselbe passiert mit
trap
Auch. Die folgende Logik würde aus den oben genannten Gründen nicht funktionieren.trap 'echo error' ERR (false)
Grundlegende Fehlerbehandlung
Wenn Ihr Testfall-Runner einen Nicht-Null-Code für fehlgeschlagene Tests zurückgibt, können Sie einfach schreiben:
test_handler test_case_x; test_result=$?
if ((test_result != 0)); then
printf '%s\n' "Test case x failed" >&2 # write error message to stderr
exit 1 # or exit $test_result
fi
Oder noch kürzer:
if ! test_handler test_case_x; then
printf '%s\n' "Test case x failed" >&2
exit 1
fi
Oder die kürzeste:
test_handler test_case_x || { printf '%s\n' "Test case x failed" >&2; exit 1; }
Beenden mit dem Exit-Code von test_handler:
test_handler test_case_x || { ec=$?; printf '%s\n' "Test case x failed" >&2; exit $ec; }
Erweiterte Fehlerbehandlung
Wenn Sie einen umfassenderen Ansatz wählen möchten, können Sie einen Fehlerbehandler verwenden:
exit_if_error() {
local exit_code=$1
shift
[[ $exit_code ]] && # do nothing if no error code passed
((exit_code != 0)) && { # do nothing if error code is 0
printf 'ERROR: %s\n' "[email protected]" >&2 # we can use better logging here
exit "$exit_code" # we could also check to make sure
# error code is numeric when passed
}
}
Rufen Sie es dann auf, nachdem Sie Ihren Testfall ausgeführt haben:
run_test_case test_case_x
exit_if_error $? "Test case x failed"
oder
run_test_case test_case_x || exit_if_error $? "Test case x failed"
Die Vorteile einer Fehlerbehandlung wie exit_if_error
sind:
- Wir können die gesamte Fehlerbehandlungslogik wie Protokollierung, Drucken eines Stack-Trace, Benachrichtigung, Bereinigung usw. an einem Ort standardisieren
- indem wir den Error-Handler dazu bringen, den Fehlercode als Argument zu erhalten, können wir dem Aufrufer den Durcheinander von
if
ersparen Blöcke, die Exit-Codes auf Fehler testen - Wenn wir einen Signal-Handler haben (mit trap), können wir den Error-Handler von dort aus aufrufen
Fehlerbehandlungs- und Protokollierungsbibliothek
Hier ist eine vollständige Implementierung der Fehlerbehandlung und Protokollierung:
https://github.com/codeforester/base/blob/master/lib/stdlib.sh
Verwandte Beiträge
- Fehlerbehandlung in Bash
- Der eingebaute Befehl 'caller' im Bash Hackers Wiki
- Gibt es in Linux standardmäßige Exit-Statuscodes?
- BashFAQ/105 - Warum macht set -e (oder set -o errexit oder trap ERR) nicht das, was ich erwartet habe?
- Äquivalent zu
__FILE__
,__LINE__
in Bash - Gibt es einen TRY CATCH-Befehl in Bash
- Um einen Stack-Trace zum Error-Handler hinzuzufügen, können Sie sich diesen Beitrag ansehen:Ablaufverfolgung ausgeführter Programme, die von einem Bash-Skript aufgerufen werden
- Ignorieren spezifischer Fehler in einem Shell-Skript
- Abfangen von Fehlercodes in einer Shell-Pipe
- Wie verwalte ich die Protokollausführlichkeit in einem Shell-Skript?
- Wie werden Funktionsname und Zeilennummer in Bash protokolliert?
- Sind doppelte eckige Klammern [[ ]] gegenüber einfachen eckigen Klammern [ ] in Bash vorzuziehen?
Dies hängt davon ab, wo die Fehlermeldung gespeichert werden soll.
Sie können Folgendes tun:
echo "Error!" > logfile.log
exit 125
Oder wie folgt:
echo "Error!" 1>&2
exit 64
Wenn Sie eine Ausnahme auslösen, stoppen Sie die Ausführung des Programms.
Sie können auch so etwas wie exit xxx
verwenden wobei xxx
ist der Fehlercode, den Sie möglicherweise an das Betriebssystem zurückgeben möchten (von 0 bis 255). Hier 125
und 64
sind nur zufällige Codes, mit denen Sie aussteigen können. Wenn Sie dem Betriebssystem mitteilen müssen, dass das Programm abnormal beendet wurde (z. B. wenn ein Fehler aufgetreten ist), müssen Sie einen Nicht-Null-Exit-Code übergeben bis exit
.
Wie @chepner betonte, können Sie exit 1
ausführen , was einen nicht näher bezeichneten Fehler bedeutet .