387 Stimmen

Beenden Sie das Shell-Skript basierend auf dem Prozess-Exit-Code

Ich habe ein Shell-Skript, das eine Reihe von Befehlen ausführt. Wie mache ich das Shell-Skript beenden, wenn einer der Befehle mit einem Nicht-Null-Exit-Code beendet?

0 Stimmen

Harter Weg: Testen Sie den Wert von $? nach jedem Befehl. Einfacher Weg: Geben Sie set -e oder #!/bin/bash -e oben in Ihr Bash-Skript ein.

503voto

paxdiablo Punkte 809679

Nach jedem Befehl kann der Exit-Code in der $? Variablen gefunden werden, also hättest du so etwas:

ls -al datei.ext
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi

Man muss bei gepipeten Befehlen vorsichtig sein, da $? nur den Rückgabecode des letzten Elements im Pipe zurückgibt, also im Code:

ls -al datei.ext | sed 's/^/xx: /"

wird kein Fehlercode zurückgegeben, wenn die Datei nicht existiert (da der sed Teil der Pipeline tatsächlich funktioniert und 0 zurückgibt).

Die bash Shell bietet tatsächlich ein Array an, das in diesem Fall hilfreich sein kann, und das ist PIPESTATUS. Dieses Array hat ein Element für jede der Pipeline-Komponenten, das du einzeln abrufen kannst, z.B. ${PIPESTATUS[0]}:

pax> false | true ; echo ${PIPESTATUS[0]}
1

Bitte beachte, dass dies dir das Ergebnis des false Befehls gibt, nicht der gesamten Pipeline. Du kannst auch die gesamte Liste abrufen, um sie nach Belieben zu verarbeiten:

pax> false | true | false; echo ${PIPESTATUS[*]}
1 0 1

Wenn du den größten Fehlercode aus einer Pipeline erhalten möchtest, könntest du etwas wie folgt verwenden:

true | true | false | true | false
rcs=${PIPESTATUS[*]}; rc=0; for i in ${rcs}; do rc=$(($i > $rc ? $i : $rc)); done
echo $rc

Dies geht jedes der PIPESTATUS Elemente nacheinander durch und speichert es in rc, wenn es größer als der vorherige rc Wert war.

39 Stimmen

Die gleiche Funktion in nur einer Zeile portablen Codes: ls -al datei.ext || exit $? ( [[ ]] ist nicht portabel)

19 Stimmen

MarcH, ich denke, dass du feststellen wirst, dass [[ ]] ziemlich portabel in bash ist, wofür die Frage verschlagwortet ist :-) Seltsamerweise funktioniert ls nicht in command.com, also ist es auch nicht portabel, spekulativ, ich weiß, aber es ist die gleiche Art von Argument, das du präsentierst.

39 Stimmen

Ich weiß, dass das alt ist, aber es sollte beachtet werden, dass Sie den Exit-Code von Befehlen in einem Pipe über das Array PIPESTATUS erhalten können (d.h. ${PIPESTATUS[0]} für den ersten Befehl, ${PIPESTATUS[1]} für den zweiten oder ${PIPESTATUS[*]} für eine Liste aller Exit-Status.

225voto

Jeff Hill Punkte 67726

Wenn Sie mit $? arbeiten möchten, müssen Sie es nach jedem Befehl überprüfen, da $? nach jedem Befehlsende aktualisiert wird. Dies bedeutet, dass Sie nur den Exit-Code des letzten Prozesses in der Pipeline erhalten, wenn Sie eine Pipeline ausführen.

Ein anderer Ansatz besteht darin, Folgendes zu tun:

set -e
set -o pipefail

Wenn Sie dies oben im Shell-Skript platzieren, scheint Bash sich darum zu kümmern. Wie ein früherer Poster bemerkt hat, wird "set -e" dazu führen, dass Bash bei jedem einfachen Befehl mit einem Fehler beendet. "set -o pipefail" wird dazu führen, dass Bash auch bei einem Befehl in einer Pipeline mit einem Fehler beendet.

Sehen Sie hier oder hier für eine weitere Diskussion zu diesem Problem. Hier finden Sie den Abschnitt des Bash-Handbuchs zum set-Befehl.

6 Stimmen

Dies sollte wirklich die beste Antwort sein: Es ist viel einfacher, dies zu tun, als PIPESTATUS zu verwenden und überall Exit-Codes zu überprüfen.

2 Stimmen

#!/bin/bash -e ist der einzige Weg, um ein Shell-Skript zu starten. Sie können immer Dinge wie foo || handle_error $? verwenden, wenn Sie tatsächlich Exit-Status überprüfen müssen.

0 Stimmen

@DavisHerring Das ist in mehrerer Hinsicht falsch oder irreführend. Die Shell--e-Option hat einige überraschende Randfälle, und daher wird ihr Gebrauch in einigen Richtlinien sogar für Anfänger nicht empfohlen. Und das Angeben in der Shebang-Zeile ist brüchig; set -e im Skript selbst zu platzieren, ist robuster.

54voto

Allen Punkte 4919

"set -e" ist wahrscheinlich der einfachste Weg, um dies zu tun. Setzen Sie das einfach vor alle Befehle in Ihrem Programm.

6 Stimmen

@SwaroopCH set -e Ihr Skript wird abbrechen, wenn ein Befehl in Ihrem Skript mit einem Fehlerstatus beendet wird und Sie diesen Fehler nicht behandelt haben.

2 Stimmen

set -e entspricht zu 100% set -o errexit, was im Gegensatz zum Ersteren durchsucht werden kann. Suchen Sie nach opengroup + errexit für die offizielle Dokumentation.

31voto

Arvodan Punkte 1205

Wenn Sie in Bash einfach exit aufrufen, ohne Parameter zu übergeben, wird der Exit-Code des letzten Befehls zurückgegeben. In Kombination mit OR sollte Bash exit nur aufrufen, wenn der vorherige Befehl fehlschlägt. Aber das habe ich nicht getestet.

command1 || exit;
command2 || exit;

Bash speichert auch den Exit-Code des letzten Befehls in der Variable $?.

27voto

chemila Punkte 3985
[ $? -eq 0 ] || exit $?; # Beenden für einen nichtnullen Rückgabecode

4 Stimmen

Sollte dies nicht exit $? sein (ohne Klammern)?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X