GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Fehler in einem Bash-Skript auslösen

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 weitergeleitet

    set -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 des set -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. Die false 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ühren

    set -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 .


Linux
  1. Lernen Sie die Bash-Fehlerbehandlung anhand eines Beispiels kennen

  2. Wie debuggt man ein Bash-Skript?

  3. Typeset -a Gibt Fehler im Skript an?

  4. Bash-Skript zum Ausfüllen einer Vorlage?

  5. Bash-Skriptfehler:Integer-Ausdruck erwartet?

Bash Exit-Befehl und Exit-Codes

35 Bash-Skriptbeispiele

Fehlerbehandlung in Bash-Skripten

So führen Sie ein Bash-Skript aus

Was bedeutet set -e in einem Bash-Skript?

Führen Sie den Bash-Befehl auf der Jenkins-Pipeline aus