Wie verlasse ich ein Shell-Skript, wenn ein Teil davon fehlschlägt?

51

Wie kann ich ein Shell-Skript schreiben, das beendet wird, wenn ein Teil davon fehlschlägt? Wenn beispielsweise der folgende Codeausschnitt fehlschlägt, sollte das Skript beendet werden.

n=0
until [ $n -ge 5 ]
do
  gksu *command* && break
  n=$[$n+1]
  sleep 3
Weylyn
quelle

Antworten:

88

Ein Ansatz wäre set -e, am Anfang Ihres Skripts einzufügen. Das heißt (von help set):

  -e  Exit immediately if a command exits with a non-zero status.

Wenn also einer Ihrer Befehle fehlschlägt, wird das Skript beendet.

Alternativ können Sie explizite exitAnweisungen an den möglichen Fehlerpunkten hinzufügen :

command || exit 1
terdon
quelle
5
Ich (und das Bash-Wiki ) würde es vorziehen, wenn die Leute über die richtige Fehlerbehandlung nachdenken, anstatt die (von Design zu Design defekte IMO) set -e-Funktion zu verwenden. Hier trifft es allerdings nicht wirklich zu. Das OP möchte das Skript nach 5 fehlgeschlagenen Versuchen, den Befehl auszuführen, beenden.
Stéphane Chazelas
1
@ StéphaneChazelas Ich werde nicht mit dir darüber streiten, ob es kaputt ist, ich bin sicher, du hast recht. Das OP fragte jedoch: "Wie kann ich ein Shell-Skript schreiben, das beendet wird, wenn ein Teil davon fehlschlägt?". Warum denken Sie, dass es nach 5 Fehlern beendet wird?
terdon
weil mir keine andere möglichkeit einfällt, die frage zu interpretieren.
Stéphane Chazelas
1
@ StéphaneChazelas du hast vielleicht recht. Ich habe es wörtlich interpretiert: Wie kann das gesamte Skript beendet werden, wenn ein Teil davon fehlschlägt? Und set -edas weiß ich nur so.
Terdon
In diesem Skript-Snipet können die folgenden Befehle ausgelöst set -ewerden sleep(da breakes sich um ein spezielles Feature handelt, wird das Skript bei Fehlern in den meisten Shells beendet. Befehle in ifoder links davon &&sind davon nicht betroffen set -e, können n=...jedoch fehlschlagen, wenn sie nschreibgeschützt sind.) das würde das Skript auch ohne verlassen set -e, so dass die Interpretation ziemlich unwahrscheinlich klingt. Ich bin damit einverstanden, dass die Frage schlecht formuliert ist.
Stéphane Chazelas
17

Sie können ein Skript an einer beliebigen Stelle mit dem Schlüsselwort beenden exit. Sie können auch einen Exit - Code angeben , um andere Programme , um anzuzeigen , dass oder wie Ihr Skript fehlgeschlagen ist , zB exit 1oder exit 2usw. (Vereinbarungs Exit - Code 0 ist für den Erfolg und etwas größer als 0 bedeutet Versagen, aber auch durch Konvention, Ausfahrt Codes über 127 sind für eine abnormale Beendigung reserviert (z. B. durch ein Signal).

Die generische Konstruktion, die bei einem Ausfall beendet werden soll, ist

if [ failure condition ]; then
    exit n
fi

mit passendem failure conditionund n. In bestimmten Szenarien können Sie jedoch anders vorgehen. Für Ihren Fall interpretiere ich Ihre Frage so, dass Sie den Vorgang beenden möchten, wenn einer der fünf gksufehlgeschlagenen Aufrufe fehlschlägt. Eine Möglichkeit besteht darin, eine solche Funktion zu verwenden

function try_command {
    for i in 1 2 3 4 5 ; do
        if gksu command ; then
            return 0
        fi
    fi
    exit 1
}

Rufen Sie dann die Schleife von auf try_command.

Es gibt (fortgeschrittenere) oder ausgefeiltere Möglichkeiten, um Ihre Frage zu beantworten. Die obige Lösung ist jedoch für Anfänger zugänglicher als beispielsweise Stephanes Lösung.

Gegenmodus
quelle
10
attempt=0
until gksu command; do
  attempt=$((attempt + 1))
  if [ "$attempt" -gt 5 ]; then
    exit 1
  fi
done

exitBeendet das Skript, sofern es nicht in einer Subshell aufgerufen wird. Befindet sich dieser Teil des Skripts in einer Subshell, z. B. weil er sich in (...)oder innerhalb einer Pipeline befindet, $(...)wird nur diese Subshell beendet .

Wenn Sie in diesem Fall möchten, dass das Skript zusätzlich zur Subshell beendet wird, müssen Sie exitdiese Subshell beenden.

Zum Beispiel hier mit 2 verschachtelten Ebenen von Subshells:

(
  life=hard
  output=$(
    echo blah
    [ "$life" = easy ] || exit 1 # exit subshell
    echo blih not run
  ) || exit # if the subshell exits with a non-zero exit status,
            # exit as well with the same exit status

  echo not run either
) || exit # if the subshell exits with a non-zero exit status,
          # exit as well with the same exit status

Es kann schwieriger werden, wenn die Subshell Teil einer Pipeline ist. bashhat ein spezielles $PIPESTATUSArray, ähnlich dem zsh, $pipestatusdas Ihnen hier helfen kann:

{
   echo foo
   exit 1
   echo bar
} | wc -c
subshell_ret=${PIPESTATUS[0]}
if [ "$subshell_ret" -ne 0 ]; then
  exit "$subshell_ret"
fi
Stéphane Chazelas
quelle
3

Die Falle führt beim Empfang eines Signals eine Aktion aus.

trap "echo EXIT;  exit" 0
trap "echo HUP;   exit" 1
trap "echo CTL-C; exit" 2
trap "echo QUIT;  exit" 3
trap "echo ERR;   exit" ERR
n=0
until [ $n -ge 5 ]
do
  n=$[$n+1]
  echo $n
  sleep 3
done

Führen Sie dies aus und lassen Sie es normal beenden. Es wird auf Signal 0 abgefangen.

EXIT

Führen Sie es erneut aus und unterbrechen Sie mit ^ C. Es fängt sowohl das Signal 2 als auch das Signal 0 ab.

CTL-C
EXIT

Ein Ausgangsstatus ungleich Null wird bei ERR abgefangen

ERR
EXIT
RobP
quelle