
Ich habe ein Bash-Skript, das verwendet wird, set -o errexit
sodass im Fehlerfall das gesamte Skript an der Fehlerstelle beendet wird.
Das Skript führt einen curl
Befehl aus, der manchmal die gewünschte Datei nicht abrufen kann. Wenn dies jedoch geschieht, wird das Skript nicht mit einem Fehler beendet.
Ich habe eine for
Schleife hinzugefügt
- Warten Sie einige Sekunden und wiederholen Sie dann den
curl
Befehl - Verwenden Sie es
false
am Ende der For-Schleife, um einen standardmäßigen Beendigungsstatus ungleich Null zu definieren. Wenn der Curl-Befehl erfolgreich ist, wird die Schleife abgebrochen und der Beendigungsstatus des letzten Befehls sollte Null sein.
#! /bin/bash
set -o errexit
# ...
for (( i=1; i<5; i++ ))
do
echo "attempt number: "$i
curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
if [ -f ~/.vim/autoload/pathogen.vim ]
then
echo "file has been retrieved by curl, so breaking now..."
break;
fi
echo "curl'ed file doesn't yet exist, so now will wait 5 seconds and retry"
sleep 5
# exit with non-zero status so main script will errexit
false
done
# rest of script .....
Das Problem besteht darin, dass curl
die Schleife den Befehl fünfmal wiederholt, wenn der Befehl fehlschlägt. Wenn alle Versuche erfolglos sind, wird die For-Schleife beendet und das Hauptskript fortgesetzt, anstatt auszulösen errexit
.
Wie kann ich das gesamte Skript beenden, wenn diese curl
Anweisung fehlschlägt?
Antwort1
Ersetzen:
done
mit:
done || exit 1
Dies führt dazu, dass der Code beendet wird, wenn die for
Schleife mit einem Exit-Code ungleich Null beendet wird.
Als kleine Anmerkung: Das 1
in exit 1
ist nicht erforderlich. Ein einfacher exit
Befehl würde mit dem Beendigungsstatus des zuletzt ausgeführten Befehls beendet, der false
(Code=1) wäre, wenn der Download fehlschlägt. Wenn der Download erfolgreich ist, ist der Beendigungscode der Schleife der Beendigungscode des echo
Befehls. echo
Normalerweise wird mit Code=0 beendet, was Erfolg signalisiert. In diesem Fall ||
wird das nicht ausgelöst und der exit
Befehl wird nicht ausgeführt.
Beachten Sie abschließend, dass set -o errexit
es viele Überraschungen geben kann. Eine Diskussion der Vor- und Nachteile finden Sie unterGregs FAQ Nr. 105.
Dokumentation
Aus man bash
:
für((Ausdruck1; Ausdruck2; Ausdruck3));TunListe ; Erledigt
Zuerst wird der arithmetische Ausdruck expr1 gemäß den unten unter ARITHMETISCHE AUSWERTUNG beschriebenen Regeln ausgewertet. Der arithmetische Ausdruck expr2 wird dann wiederholt ausgewertet, bis er Null ergibt. Jedes Mal, wenn expr2 einen Wert ungleich Null ergibt, wird list ausgeführt und der arithmetische Ausdruck expr3 wird ausgewertet. Wenn ein Ausdruck weggelassen wird, verhält er sich so, als ob er 1 ergibt. Der Rückgabewert ist der Beendigungsstatus des letzten ausgeführten Befehls in der Liste oder „false“, wenn einer der Ausdrücke ungültig ist.[Betonung hinzugefügt]
Antwort2
Wenn Sie die Schleife lieber beim ersten Fehler stoppen und außerdem vermeiden möchten set -e
, können Sie Folgendes tun:
for i in `seq 1 10`; do
run_command || exit 1;
done
Antwort3
Wenn Sie errexit
dies festgelegt haben, false
sollte die Anweisung dazu führen, dass das Skript sofort beendet wird. Dasselbe gilt, wenn der curl
Befehl fehlgeschlagen ist.
Ihr Beispielskript sollte, wie geschrieben, nach dem ersten curl
Befehlsfehler beim ersten Aufruf beendet werden false
, wenn „errexit“ gesetzt ist.
Um zu sehen, wie es funktioniert (ich verwende die Abkürzung -e
für to set errexit
):
$ ( set -e; false; echo still here )
$
$ ( set +e; false; echo still here )
still here
$
Wenn der curl
Befehl also mehr als einmal ausgeführt wird, ist dieses Skript nicht errexit
festgelegt.
Antwort4
set -o errexit
kann in Schleifen und Unter-Shells knifflig sein, da man den Weg zurück aus dem Prozess passieren muss.
Das Unterbrechen einer Schleife (selbst im Normalbetrieb) gilt als schlechte Praxis. Sie können mich als altmodisch bezeichnen, weil ich für zwei Bedingungen eine while-Schleife einer for-Schleife vorziehe, aber ich finde es besser, es zu lesen:
i=1
RET=-1
while [ $i -le 5 ] && [ $RET -ne 0 ]; do
[ $i -eq 1 ] || sleep 5
echo "attempt number: "$i
curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
RET=$?
i=$((i+1))
done
exit $RET