Beenden Sie das Shell-Skript basierend auf dem Prozess-Exit-Code
378
Ich habe ein Shell-Skript, das eine Reihe von Befehlen ausführt. Wie kann ich das Shell-Skript beenden, wenn einer der Befehle mit einem Exit-Code ungleich Null beendet wird?
Ich habe angenommen, dass Sie bash verwenden, aber wenn es sich um eine ganz andere Shell handelt, können Sie dies in Ihrem Beitrag angeben?
Martin W
Harte Methode: Testen Sie den Wert von $?nach jedem Befehl. Einfache Methode: Setzen Sie set -eoder ganz #!/bin/bash -eoben in Ihr Bash-Skript.
mwfearnley
Antworten:
494
Nach jedem Befehl befindet sich der Exit-Code in der $?Variablen, sodass Sie Folgendes haben:
ls -al file.ext
rc=$?;if[[ $rc !=0]];then exit $rc;fi
Sie müssen vorsichtig mit Piped-Befehlen sein, da der $?einzige nur den Rückkehrcode des letzten Elements in der Pipe enthält, also im Code:
ls -al file.ext | sed 's/^/xx: /"
gibt keinen Fehlercode zurück, wenn die Datei nicht vorhanden ist (da der sedTeil der Pipeline tatsächlich funktioniert und 0 zurückgibt).
Die bashShell stellt tatsächlich ein Array bereit, das in diesem Fall hilfreich sein kann PIPESTATUS. Dieses Array verfügt über ein Element für jede Pipeline-Komponente, auf das Sie einzeln zugreifen können ${PIPESTATUS[0]}:
pax> false | true ; echo ${PIPESTATUS[0]}1
Beachten Sie, dass Sie damit das Ergebnis des falseBefehls erhalten, nicht die gesamte Pipeline. Sie können auch die gesamte Liste nach Belieben verarbeiten lassen:
Gleiche Funktion in nur einer Zeile des tragbaren Codes: ls -al file.ext || exit $?([[]] ist nicht tragbar)
MarcH
19
MarcH, ich denke, Sie werden feststellen, dass [[ ]]das ziemlich portabel bashist, was die Frage ist :-) Seltsamerweise lsfunktioniert es nicht, command.comalso ist es auch nicht portabel, ich weiß, es ist die gleiche Art von Argument, die Sie haben vorhanden.
Paxdiablo
39
Ich weiß, dass dies uralt ist, aber es sollte beachtet werden, dass Sie den Exit-Code von Befehlen in einer Pipe über das Array PIPESTATUS${PIPESTATUS[0]}${PIPESTATUS[1]}${PIPESTATUS[*]}
abrufen können
11
Es muss betont werden, dass elegantes und idiomatisches Shell-Scripting sehr selten $?direkt untersucht werden muss. Normalerweise möchten Sie so etwas wie das, if ls -al file.ext; then : nothing; else exit $?; fiwas natürlich wie @MarcH sagt, äquivalent ist, ls -al file.ext || exit $?aber wenn die Klauseln thenoder elseetwas komplexer sind, ist es wartbarer.
Tripleee
9
[[ $rc != 0 ]]wird Ihnen einen 0: not foundoder 1: not foundFehler geben. Dies sollte in geändert werden [ $rc -ne 0 ]. Auch rc=$?könnte dann entfernt und einfach verwendet werden [ $? -ne 0 ].
CurtisLeeBolin
223
Wenn Sie mit $? Arbeiten möchten, müssen Sie dies nach jedem Befehl überprüfen, da $? wird nach jedem Befehlsausgang aktualisiert. Dies bedeutet, dass Sie beim Ausführen einer Pipeline nur den Exit-Code des letzten Prozesses in der Pipeline erhalten.
Ein anderer Ansatz besteht darin, dies zu tun:
set-e
set-o pipefail
Wenn Sie dies oben in das Shell-Skript einfügen, wird Bash dies anscheinend für Sie erledigen. Wie in einem vorherigen Poster erwähnt, führt "set -e" dazu, dass bash bei einem einfachen Befehl mit einem Fehler beendet wird. "set -o pipefail" führt dazu, dass bash auch bei einem Befehl in einer Pipeline mit einem Fehler beendet wird.
Weitere Informationen zu diesem Problem finden Sie hier oder hier . Hier ist der Abschnitt mit dem Bash-Handbuch zum eingebauten Set.
Dies sollte wirklich die beste Antwort sein: Es ist viel, viel einfacher, dies zu tun, als PIPESTATUSüberall Exit-Codes zu verwenden und zu überprüfen.
Candu
2
#!/bin/bash -eist die einzige Möglichkeit, ein Shell-Skript zu starten. Sie können immer Dinge verwenden, z. B. foo || handle_error $?wenn Sie den Exit-Status tatsächlich überprüfen müssen.
Davis Herring
53
" set -e" ist wahrscheinlich der einfachste Weg, dies zu tun. Stellen Sie das einfach vor die Befehle in Ihrem Programm.
@SwaroopCH set -eIhr Skript wird abgebrochen , wenn ein Befehl in Ihrem Skript mit dem Fehlerstatus beendet wird und Sie diesen Fehler nicht behandelt haben.
Andrew
2
set -eist 100% äquivalent, zu set -o errexitdem im Gegensatz zu ersteren gesucht werden kann. Suche nach opengroup + errexit für offizielle Dokumentation.
MarcH
30
Wenn Sie in der Bash nur exit ohne Parameter aufrufen, wird der Exit-Code des letzten Befehls zurückgegeben. In Kombination mit OR sollte die Bash nur dann exit aufrufen, wenn der vorherige Befehl fehlschlägt. Aber ich habe das nicht getestet.
Befehl1 || Ausfahrt;
Befehl2 || Ausfahrt;
Der Bash speichert auch den Exit-Code des letzten Befehls in der Variablen $?.
Beachten Sie zunächst, dass der cmd1Exit-Code ungleich Null sein kann und dennoch keinen Fehler bedeutet. Dies geschieht zum Beispiel in
cmd | head -1
Möglicherweise wird ein Exit-Status von 141 (oder 269 mit ksh93) von beobachtet cmd1, der jedoch cmddurch ein SIGPIPE-Signal unterbrochen wurde, als
head -1er nach dem Lesen einer Zeile beendet wurde.
Den Exit-Status der Elemente einer Pipeline kennen
cmd1 | cmd2 | cmd3
ein. mit zsh:
Die Exit-Codes sind im speziellen Array pipestatus enthalten.
cmd1Exit-Code ist in $pipestatus[1], cmd3Exit-Code in
$pipestatus[3], so dass $?das immer das gleiche ist wie
$pipestatus[-1].
b. mit bash:
Die Exit-Codes werden im PIPESTATUSspeziellen Array bereitgestellt .
cmd1Exit-Code ist in ${PIPESTATUS[0]}, cmd3Exit-Code in
${PIPESTATUS[2]}, so dass $?das immer das gleiche ist wie
${PIPESTATUS: -1}.
# this will trap any errors or commands with non-zero exit status# by calling function catch_errors()
trap catch_errors ERR;## ... the rest of the script goes here# function catch_errors(){# do whatever on errors# #
echo "script aborted, because of errors";
exit 0;}
+1 Dies war die einfachste Lösung, nach der ich gesucht habe. Darüber hinaus können Sie auch schreiben, if (! command)wenn Sie vom Befehl einen Fehlercode ungleich Null erwarten.
Berci
Dies ist für sequenzielle Befehle. Was ist, wenn ich diese 3 parallel starten und alle töten möchte, wenn einer von ihnen ausfällt?
Vasile Surdu
4
##------------------------------------------------------------------------------# run a command on failure exit with message# doPrintHelp: doRunCmdOrExit "$cmd"# call by:# set -e ; doRunCmdOrExit "$cmd" ; set +e#------------------------------------------------------------------------------
doRunCmdOrExit(){
cmd="$@";
doLog "DEBUG running cmd or exit: \"$cmd\""
msg=$($cmd 2>&1)
export exit_code=$?# if occured during the execution exit with error
error_msg="Failed to run the command:
\"$cmd\" with the output:
\"$msg\" !!!"if[ $exit_code -ne 0];then
doLog "ERROR $msg"
doLog "FATAL $msg"
doExit "$exit_code""$error_msg"else#if no errors occured just log the message
doLog "DEBUG : cmdoutput : \"$msg\""
doLog "INFO $msg"fi}#eof func doRunCmdOrExit
$?
nach jedem Befehl. Einfache Methode: Setzen Sieset -e
oder ganz#!/bin/bash -e
oben in Ihr Bash-Skript.Antworten:
Nach jedem Befehl befindet sich der Exit-Code in der
$?
Variablen, sodass Sie Folgendes haben:Sie müssen vorsichtig mit Piped-Befehlen sein, da der
$?
einzige nur den Rückkehrcode des letzten Elements in der Pipe enthält, also im Code:gibt keinen Fehlercode zurück, wenn die Datei nicht vorhanden ist (da der
sed
Teil der Pipeline tatsächlich funktioniert und 0 zurückgibt).Die
bash
Shell stellt tatsächlich ein Array bereit, das in diesem Fall hilfreich sein kannPIPESTATUS
. Dieses Array verfügt über ein Element für jede Pipeline-Komponente, auf das Sie einzeln zugreifen können${PIPESTATUS[0]}
:Beachten Sie, dass Sie damit das Ergebnis des
false
Befehls erhalten, nicht die gesamte Pipeline. Sie können auch die gesamte Liste nach Belieben verarbeiten lassen:Wenn Sie den größten Fehlercode aus einer Pipeline erhalten möchten, können Sie Folgendes verwenden:
Dies geht nacheinander durch jedes der
PIPESTATUS
Elemente und speichert es,rc
wenn es größer als der vorherigerc
Wert war.quelle
ls -al file.ext || exit $?
([[]] ist nicht tragbar)[[ ]]
das ziemlich portabelbash
ist, was die Frage ist :-) Seltsamerweisels
funktioniert es nicht,command.com
also ist es auch nicht portabel, ich weiß, es ist die gleiche Art von Argument, die Sie haben vorhanden.PIPESTATUS
${PIPESTATUS[0]}
${PIPESTATUS[1]}
${PIPESTATUS[*]}
$?
direkt untersucht werden muss. Normalerweise möchten Sie so etwas wie das,if ls -al file.ext; then : nothing; else exit $?; fi
was natürlich wie @MarcH sagt, äquivalent ist,ls -al file.ext || exit $?
aber wenn die Klauselnthen
oderelse
etwas komplexer sind, ist es wartbarer.[[ $rc != 0 ]]
wird Ihnen einen0: not found
oder1: not found
Fehler geben. Dies sollte in geändert werden[ $rc -ne 0 ]
. Auchrc=$?
könnte dann entfernt und einfach verwendet werden[ $? -ne 0 ]
.Wenn Sie mit $? Arbeiten möchten, müssen Sie dies nach jedem Befehl überprüfen, da $? wird nach jedem Befehlsausgang aktualisiert. Dies bedeutet, dass Sie beim Ausführen einer Pipeline nur den Exit-Code des letzten Prozesses in der Pipeline erhalten.
Ein anderer Ansatz besteht darin, dies zu tun:
Wenn Sie dies oben in das Shell-Skript einfügen, wird Bash dies anscheinend für Sie erledigen. Wie in einem vorherigen Poster erwähnt, führt "set -e" dazu, dass bash bei einem einfachen Befehl mit einem Fehler beendet wird. "set -o pipefail" führt dazu, dass bash auch bei einem Befehl in einer Pipeline mit einem Fehler beendet wird.
Weitere Informationen zu diesem Problem finden Sie hier oder hier . Hier ist der Abschnitt mit dem Bash-Handbuch zum eingebauten Set.
quelle
PIPESTATUS
überall Exit-Codes zu verwenden und zu überprüfen.#!/bin/bash -e
ist die einzige Möglichkeit, ein Shell-Skript zu starten. Sie können immer Dinge verwenden, z. B.foo || handle_error $?
wenn Sie den Exit-Status tatsächlich überprüfen müssen."
set -e
" ist wahrscheinlich der einfachste Weg, dies zu tun. Stellen Sie das einfach vor die Befehle in Ihrem Programm.quelle
set -e
Ihr Skript wird abgebrochen , wenn ein Befehl in Ihrem Skript mit dem Fehlerstatus beendet wird und Sie diesen Fehler nicht behandelt haben.set -e
ist 100% äquivalent, zuset -o errexit
dem im Gegensatz zu ersteren gesucht werden kann. Suche nach opengroup + errexit für offizielle Dokumentation.Wenn Sie in der Bash nur exit ohne Parameter aufrufen, wird der Exit-Code des letzten Befehls zurückgegeben. In Kombination mit OR sollte die Bash nur dann exit aufrufen, wenn der vorherige Befehl fehlschlägt. Aber ich habe das nicht getestet.
Der Bash speichert auch den Exit-Code des letzten Befehls in der Variablen $?.
quelle
quelle
exit $?
(keine Eltern)?http://cfaj.freeshell.org/shell/cus-faq-2.html#11
Wie bekomme ich den Exit-Code von
cmd1
incmd1|cmd2
Beachten Sie zunächst, dass der
cmd1
Exit-Code ungleich Null sein kann und dennoch keinen Fehler bedeutet. Dies geschieht zum Beispiel inMöglicherweise wird ein Exit-Status von 141 (oder 269 mit ksh93) von beobachtet
cmd1
, der jedochcmd
durch ein SIGPIPE-Signal unterbrochen wurde, alshead -1
er nach dem Lesen einer Zeile beendet wurde.Den Exit-Status der Elemente einer Pipeline kennen
cmd1 | cmd2 | cmd3
ein. mit zsh:
Die Exit-Codes sind im speziellen Array pipestatus enthalten.
cmd1
Exit-Code ist in$pipestatus[1]
,cmd3
Exit-Code in$pipestatus[3]
, so dass$?
das immer das gleiche ist wie$pipestatus[-1]
.b. mit bash:
Die Exit-Codes werden im
PIPESTATUS
speziellen Array bereitgestellt .cmd1
Exit-Code ist in${PIPESTATUS[0]}
,cmd3
Exit-Code in${PIPESTATUS[2]}
, so dass$?
das immer das gleiche ist wie${PIPESTATUS: -1}
....
Weitere Details finden Sie unter folgendem Link .
quelle
für Bash:
quelle
In Bash ist das einfach, binde sie einfach mit && zusammen:
Sie können auch das verschachtelte if-Konstrukt verwenden:
quelle
if (! command)
wenn Sie vom Befehl einen Fehlercode ungleich Null erwarten.quelle
$*
; Verwenden Sie"$@"
stattdessen, um Leerzeichen und Platzhalter zu erhalten.