Wie man Fehler in Bash-Skripten unter Linux abfängt

Veröffentlicht: 2022-08-27
Linux-Laptop mit einer Bash-Eingabeaufforderung
fatmawati achmad zaenuri/Shutterstock.com

Standardmäß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.

Anpassen von Parametervalidierungsfehlern in PowerShell
ZUGEHÖRIGE Fehler bei der Validierung von Anpassungsparametern in PowerShell

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 

Ein Skript mit chmod ausführbar machen

Wenn wir das Skript ausführen, sehen wir die erwartete Fehlermeldung.

 ./bad_command.sh 

Überprüfen des Beendigungsstatus eines Befehls, um festzustellen, ob ein Fehler aufgetreten ist

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 $? 

Verwenden des logischen OR-Operators zum Aufrufen der Fehlerbehandlungsroutine in einem Skript

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.

So verwenden Sie set und pipefail in Bash-Skripten unter Linux
RELATED How To Use set and pipefail in Bash Scripts on Linux

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 

Verwenden des set-Befehls in einem Skript, um das Skript zu beenden, wenn ein Fehler auftritt

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 

Trap in einem Skript verwenden, um Strg+c abzufangen

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 

Verwenden von trap mit ERR zum Abfangen von Fehlern in einem Skript

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