Wie man Fehler in Bash-Skripten unter Linux abfängt
Veröffentlicht: 2022-08-27Standardmäßig meldet ein Bash-Skript unter Linux einen Fehler, läuft aber weiter. Wir zeigen Ihnen, wie Sie selbst mit Fehlern umgehen können, damit Sie entscheiden können, was als nächstes passieren muss.
Fehlerbehandlung in Skripten
Der Umgang mit Fehlern ist Teil der Programmierung. Selbst wenn Sie fehlerfreien Code schreiben, können Sie immer noch auf Fehlerbedingungen stoßen. Die Umgebung auf Ihrem Computer ändert sich im Laufe der Zeit, wenn Sie Software installieren und deinstallieren, Verzeichnisse erstellen und Upgrades und Updates durchführen.
Beispielsweise kann ein Skript, das zuvor ohne Probleme ausgeführt wurde, in Schwierigkeiten geraten, wenn sich Verzeichnispfade ändern oder Berechtigungen für eine Datei geändert werden. Die Standardaktion der Bash-Shell besteht darin, eine Fehlermeldung auszugeben und mit der Ausführung des Skripts fortzufahren. Dies ist ein gefährlicher Ausfall.
Wenn die fehlgeschlagene Aktion für eine andere Verarbeitung oder Aktion, die später in Ihrem Skript stattfindet, von entscheidender Bedeutung ist, wird diese kritische Aktion nicht erfolgreich sein. Wie katastrophal das ausfällt, hängt davon ab, was Ihr Skript zu tun versucht.
Ein robusteres Schema würde Fehler erkennen und das Skript arbeiten lassen, wenn es heruntergefahren oder versucht werden müsste, den Fehlerzustand zu beheben. Wenn beispielsweise ein Verzeichnis oder eine Datei fehlt, kann es ausreichend sein, sie vom Skript neu erstellen zu lassen.
Wenn das Skript auf ein Problem gestoßen ist, von dem es nicht wiederhergestellt werden kann, kann es heruntergefahren werden. Wenn das Skript heruntergefahren werden muss, kann es die erforderliche Bereinigung durchführen, z. B. temporäre Dateien entfernen oder die Fehlerbedingung und den Grund für das Herunterfahren in eine Protokolldatei schreiben.
Ermitteln des Exit-Status
Befehle und Programme generieren einen Wert, der beim Beenden an das Betriebssystem gesendet wird. Dies wird als Ausgangsstatus bezeichnet. Es hat einen Wert von Null, wenn keine Fehler aufgetreten sind, oder einen Wert ungleich Null, wenn ein Fehler aufgetreten ist.
Wir können den Exit-Status – auch bekannt als Rückgabecode – der vom Skript verwendeten Befehle überprüfen und feststellen, ob der Befehl erfolgreich war oder nicht.
In Bash ist null gleich wahr. Wenn die Antwort des Befehls nicht wahr ist, wissen wir, dass ein Problem aufgetreten ist, und wir können entsprechende Maßnahmen ergreifen.
Kopieren Sie dieses Skript in einen Editor und speichern Sie es in einer Datei namens „bad_command.sh“.
#!/bin/bash if ( ! bad_command ); dann echo "bad_command hat einen Fehler gemeldet." Ausgang 1 fi
Sie müssen das Skript mit dem Befehl chmod
ausführbar machen. Dies ist ein Schritt, der erforderlich ist, um jedes Skript ausführbar zu machen. Wenn Sie also die Skripts auf Ihrem eigenen Computer ausprobieren möchten, denken Sie daran, dies für jedes von ihnen zu tun. Ersetzen Sie jeweils den Namen des entsprechenden Skripts.
chmod +x bad_command.sh
Wenn wir das Skript ausführen, sehen wir die erwartete Fehlermeldung.
./bad_command.sh
Es gibt weder einen Befehl wie „bad_command“, noch ist es der Name einer Funktion innerhalb des Skripts. Es kann nicht ausgeführt werden, daher ist die Antwort nicht Null. Wenn die Antwort nicht Null ist – das Ausrufezeichen wird hier als logischer NOT
-Operator verwendet – wird der Hauptteil der if
-Anweisung ausgeführt.
In einem realen Skript könnte dies das Skript beenden, was in unserem Beispiel der Fall ist, oder es könnte versuchen, den Fehlerzustand zu beheben.
Es sieht möglicherweise so aus, als wäre die Linie von exit 1
überflüssig. Schließlich enthält das Skript nichts anderes und es wird sowieso beendet. Aber mit dem exit
-Befehl können wir einen Exit-Status an die Shell zurückgeben. Wenn unser Skript jemals von einem zweiten Skript aus aufgerufen wird, weiß dieses zweite Skript, dass dieses Skript auf Fehler gestoßen ist.
Sie können den logischen OR
-Operator mit dem Exit-Status eines Befehls verwenden und einen anderen Befehl oder eine Funktion in Ihrem Skript aufrufen, wenn vom ersten Befehl eine Antwort ungleich Null kommt.
Befehl_1 || Befehl_2
Dies funktioniert, weil entweder der erste Befehl ausgeführt wird OR
der zweite. Der Befehl ganz links wird zuerst ausgeführt. Wenn es erfolgreich ist, wird der zweite Befehl nicht ausgeführt. Aber wenn der erste Befehl fehlschlägt, wird der zweite Befehl ausgeführt. So können wir Code wie folgt strukturieren. Dies ist „logisch-oder./sch.“
#!/bin/bash error_handler() { echo "Fehler: ($?) $1" Ausgang 1 } schlechter_Befehl || error_handler "bad_command fehlgeschlagen, Zeile: ${LINENO}"
Wir haben eine Funktion namens error_handler
definiert. Dies gibt den Exit-Status des fehlgeschlagenen Befehls aus, der in der Variablen $?
und eine Textzeile, die ihr übergeben wird, wenn die Funktion aufgerufen wird. Dies wird in der Variablen $1
. Die Funktion beendet das Skript mit einem Exit-Status von eins.
Das Skript versucht, bad_command
was offensichtlich fehlschlägt, also der Befehl rechts vom logischen OR
-Operator ||
, wird ausgeführt. Dadurch wird die Funktion error_handler
und eine Zeichenfolge übergeben, die den fehlgeschlagenen Befehl benennt und die Zeilennummer des fehlgeschlagenen Befehls enthält.
Wir führen das Skript aus, um die Fehlerbehandlungsmeldung anzuzeigen, und überprüfen dann den Beendigungsstatus des Skripts mit echo.
./logisch-oder.sh
Echo $?
Unsere kleine error_handler
Funktion liefert den Exit-Status des Versuchs, bad_command
, den Namen des Befehls und die Zeilennummer. Dies sind nützliche Informationen, wenn Sie ein Skript debuggen.
Der Exit-Status des Skripts ist eins. Der von error_handler
gemeldete Exit-Status 127 bedeutet „Befehl nicht gefunden“. Wenn wir wollten, könnten wir das als Exit-Status des Skripts verwenden, indem wir es an den exit
Befehl übergeben.
Ein anderer Ansatz wäre, error_handler
zu erweitern, um die verschiedenen möglichen Werte des Exit-Status zu prüfen und entsprechend verschiedene Aktionen auszuführen, indem dieser Konstrukttyp verwendet wird:
exit_code=$? wenn [ $exit_code -eq 1 ]; dann Echo "Operation nicht erlaubt" elif [ $exit_code -eq 2 ]; dann echo "Missbrauch von Shell Builtins" . . . elif [ $status -eq 128 ]; dann echo "Ungültiges Argument" fi
Verwenden von set, um einen Exit zu erzwingen
Wenn Sie wissen, dass Ihr Skript beendet werden soll, wenn ein Fehler auftritt, können Sie dies erzwingen. Das bedeutet, dass Sie auf eine Bereinigung – oder auch auf weitere Schäden – verzichten, da Ihr Skript beendet wird, sobald es einen Fehler erkennt.
Verwenden Sie dazu den Befehl set
mit der Option -e
(Fehler). Dies weist das Skript an, immer dann zu beenden, wenn ein Befehl fehlschlägt oder einen Exit-Code größer als Null zurückgibt. Außerdem stellt die Verwendung der Option -E
sicher, dass die Fehlererkennung und das Trapping in Shell-Funktionen funktionieren.
Um auch nicht initialisierte Variablen abzufangen, fügen Sie die Option -u
(unset) hinzu. Um sicherzustellen, dass Fehler in über Pipe geleiteten Sequenzen erkannt werden, fügen Sie die Option -o pipefail
. Ohne dies ist der Exit-Status einer über Pipe geleiteten Befehlsfolge der Exit-Status des letzten Befehls in der Folge. Ein fehlgeschlagener Befehl in der Mitte der geleiteten Sequenz würde nicht erkannt werden. Die Option -o pipefail
muss in der Liste der Optionen enthalten sein.
Die Sequenz, die am Anfang Ihres Skripts hinzugefügt werden muss, lautet:
set -Eeuo pipefail
Hier ist ein kurzes Skript namens „unset-var.sh“ mit einer nicht gesetzten Variable darin.
#!/bin/bash set -Eeou pipefail echo "$unset_variable" echo "Sehen wir diese Linie?"
Wenn wir das Skript ausführen, wird die unset_variable als nicht initialisierte Variable erkannt und das Skript wird beendet.
./unset-var.sh
Der zweite echo
Befehl wird nie ausgeführt.
Trap mit Fehlern verwenden
Mit dem Bash-Trap-Befehl können Sie einen Befehl oder eine Funktion benennen, die aufgerufen werden soll, wenn ein bestimmtes Signal ausgelöst wird. Normalerweise wird dies verwendet, um Signale wie SIGINT
, die ausgelöst werden, wenn Sie die Tastenkombination Strg+C drücken. Dieses Skript ist „sigint.sh“.
#!/bin/bash trap "echo -e '\nBeendet durch Strg+c'; exit" SIGINT Zähler=0 während wahr tun echo "Schleifennummer:" $((++counter)) schlafen 1 erledigt
Der trap
Befehl enthält einen echo
-Befehl und den exit
-Befehl. Es wird ausgelöst, wenn SIGINT
ausgelöst wird. Der Rest des Skripts ist eine einfache Schleife. Wenn Sie das Skript ausführen und Strg+C drücken, sehen Sie die Nachricht von der trap
Definition und das Skript wird beendet.
./signt.sh
Wir können trap
mit dem ERR
-Signal verwenden, um auftretende Fehler abzufangen. Diese können dann einem Befehl oder einer Funktion zugeführt werden. Das ist „trap.sh“. Wir senden Fehlerbenachrichtigungen an eine Funktion namens error_handler
.
#!/bin/bash trap 'error_handler $? $LINENO' ERR error_handler() { echo "Fehler: ($1) ist auf $2 aufgetreten" } hauptsächlich() { echo "Innerhalb der main()-Funktion" schlechter_befehl zweite dritte Ausgang $? } zweite() { echo "Nach dem Aufruf von main()" echo "Innerhalb der zweiten () Funktion" } dritte() { echo "Innerhalb der Third()-Funktion" } hauptsächlich
Der Großteil des Skripts befindet sich in der main
, die die second
und third
Funktion aufruft. Wenn ein Fehler auftritt – in diesem Fall, weil bad_command
nicht existiert – leitet die trap
-Anweisung den Fehler an die error_handler
Funktion weiter. Sie übergibt den Beendigungsstatus des fehlgeschlagenen Befehls und die Zeilennummer an die error_handler
Funktion.
./trap.sh
Unsere error_handler
Funktion listet einfach die Details des Fehlers im Terminalfenster auf. Wenn Sie möchten, können Sie der Funktion einen exit
-Befehl hinzufügen, damit das Skript beendet wird. Oder Sie könnten eine Reihe von if/elif/fi
Anweisungen verwenden, um verschiedene Aktionen für verschiedene Fehler auszuführen.
Einige Fehler können möglicherweise behoben werden, andere erfordern möglicherweise, dass das Skript angehalten wird.
Ein letzter Tipp
Das Abfangen von Fehlern bedeutet oft, den Dingen vorzubeugen, die schief gehen können, und Code einzufügen, um diese Eventualitäten zu behandeln, falls sie auftreten sollten. Dazu müssen Sie sicherstellen, dass der Ausführungsablauf und die interne Logik Ihres Skripts korrekt sind.
Wenn Sie diesen Befehl verwenden, um Ihr Skript auszuführen, zeigt Bash Ihnen eine Ablaufverfolgungsausgabe, während das Skript ausgeführt wird:
bash -x your-script.sh
Bash schreibt die Trace-Ausgabe in das Terminalfenster. Es zeigt jeden Befehl mit seinen Argumenten – falls vorhanden. Dies geschieht, nachdem die Befehle erweitert wurden, aber bevor sie ausgeführt werden.
Es kann eine enorme Hilfe beim Aufspüren schwer fassbarer Fehler sein.
VERWANDT: So validieren Sie die Syntax eines Linux-Bash-Skripts, bevor Sie es ausführen