Wie kann ich erreichen, dass dieses Skript basierend auf dem Ergebnis einer For-Schleife mit einem Fehler beendet wird?

Wie kann ich erreichen, dass dieses Skript basierend auf dem Ergebnis einer For-Schleife mit einem Fehler beendet wird?

Ich habe ein Bash-Skript, das verwendet wird, set -o errexitsodass im Fehlerfall das gesamte Skript an der Fehlerstelle beendet wird.
Das Skript führt einen curlBefehl 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 forSchleife hinzugefügt

  1. Warten Sie einige Sekunden und wiederholen Sie dann den curlBefehl
  2. Verwenden Sie es falseam 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 curldie 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 curlAnweisung fehlschlägt?

Antwort1

Ersetzen:

done

mit:

done || exit 1

Dies führt dazu, dass der Code beendet wird, wenn die forSchleife mit einem Exit-Code ungleich Null beendet wird.

Als kleine Anmerkung: Das 1in exit 1ist nicht erforderlich. Ein einfacher exitBefehl 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 echoBefehls. echoNormalerweise wird mit Code=0 beendet, was Erfolg signalisiert. In diesem Fall ||wird das nicht ausgelöst und der exitBefehl wird nicht ausgeführt.

Beachten Sie abschließend, dass set -o errexites 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 errexitdies festgelegt haben, falsesollte die Anweisung dazu führen, dass das Skript sofort beendet wird. Dasselbe gilt, wenn der curlBefehl fehlgeschlagen ist.

Ihr Beispielskript sollte, wie geschrieben, nach dem ersten curlBefehlsfehler beim ersten Aufruf beendet werden false, wenn „errexit“ gesetzt ist.

Um zu sehen, wie es funktioniert (ich verwende die Abkürzung -efür to ​​set errexit):

$ ( set -e;  false; echo still here )
$

$ ( set +e;  false; echo still here )
still here
$

Wenn der curlBefehl also mehr als einmal ausgeführt wird, ist dieses Skript nicht errexitfestgelegt.

Antwort4

set -o errexitkann 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

verwandte Informationen