Wie stoppe ich das Bash-Skript, wenn eine Bedingung fehlschlägt?

11

Hier wird gezeigt, wie Sie das ||und &&in einer einzelnen Zeile verwenden, um die Ausführung von Befehlen zu verketten: Wie kann ich in einem Bash-Skript nach apt-get-Fehlern suchen?

Ich versuche, eine Skriptausführung zu stoppen, wenn eine bestimmte Bedingung fehlschlägt.

z.B

false || echo "Obvious error because its false on left" && exit

Hier wird Obvious error because its false on the leftdie Konsole gedruckt und verlassen, was ich wollte.

true || echo "This shouldn't print" && exit

Hier gibt es keinen Echodruck, aber der exitBefehl wird auch ausgeführt, dh die Konsole ist geschlossen. Sollte der exitBefehl nicht nicht ausgeführt werden, weil der Echo-Befehl rechts nicht ausgeführt wurde? Oder wird standardmäßig eine Aussage auf der linken Seite eines &&Operators als falsch angesehen ?

Bearbeiten: Ich hätte es vorher erwähnen sollen, mein Ziel war es, den Fehler zu wiederholen und zu beenden, wenn es nicht klar war. In Bezug auf meinen speziellen Fall, Fehler beim Gruppieren von Bedingungen mit && und || abzufangen, löst die Antwort @ bodhi.zazen das Problem.

Die Antwort von @takatakatek macht die Flusskontrolle klarer und die Bash-Guide-Links sind ausgezeichnet

Die Antwort von @muru enthält eine gute Erklärung dafür, warum Sie set -e nicht verwenden sollten, wenn Sie möchten, dass benutzerdefinierte Fehlermeldungen mit Alternativen zur Verwendung von Perl und Trap ausgegeben werden. Dies ist meiner Meinung nach eine robustere Methode und ich sehe mich ab meinem zweiten Bash-Skript damit!

Kosol
quelle
Soweit ich das beurteilen kann, wird es beendet, sobald das Skript fertig ist.
Panther

Antworten:

10

Sie wollen wahrscheinlich (wie von Steeldriver hervorgehoben)

true || { echo "This shouldn't print" && exit; }

Andernfalls liest Ihr Skript jeweils eine Bedingung

a oder b und dann beenden

Ich denke du willst a oder (b und aussteigen)?

Panther
quelle
4
Eigentlich Sie müssen verwenden { ... ; }(wie @steeldriver vorgeschlagen) oder auch das exitverlässt nur die Sub - Shell, und die Eltern - Shell das Skript weiter ausgeführt wird .
Gordon Davisson
14

Das Problem ist, dass || und && überspringen nur eine nachfolgende Zeilengruppe einer Befehlskette, wenn die Bedingung fehlschlägt. Wenn Sie eine vollständige Blockstruktur schreiben, ist dies absolut sinnvoll. Was Sie geschrieben haben, wird dies:

if ! true ; then
   echo "This shouldn't print"
else
   exit
fi

Was Sie wollen, ist Folgendes:

if ! true ; then
   echo "This shouldn't print"
   exit
fi

Wenn Sie die bedingten Verknüpfungsoperatoren von Bash verwenden, ist es besser, sie nicht zu mischen. Die Logik ist beim Schreiben viel einfacher zu verstehen, und Ihre Leser werden Ihren guten Stil zu schätzen wissen.

Takatakatek
quelle
2
Ja, meiner Meinung nach ist dies viel mehr, wie Konditionierung in Bash geschrieben werden sollte
derHugo
@takatakatek Dies macht klarer, warum es in meinem Fall fehlschlägt. Ich bin damit einverstanden, dass die Logik in diesem Fall klar ist, aber nicht der Grund für die Gruppierung von Bedingungen mit && und || ist ist es zu vermeiden, sie mit bedingten Anweisungen auszudrücken?
Kosol
@kosol Ja, && und || Kombinieren Sie zwei Dinge: Testen des Exit-Status des vorherigen Befehls und Angeben eines einzelnen Befehls (oder einer Befehlsgruppe), der ausgeführt (für &&) oder übersprungen (für ||) werden soll. Sie können für sehr einfache Fälle nützlich sein, aber sie können nicht vollständig ausgedrückte if ... then ... elif ... else ... fiBlöcke ersetzen . Ich vermute, Sie wissen vielleicht nicht, dass Bash keine echten Booleschen Typen hat, die in komplexeren Sprachen vorkommen. Wenn der Exit-Status eines Befehls 0 (Null) ist, behandelt Bash dies als wahr / erfolgreich . Wenn der Exit-Status ungleich Null ist, behandelt Bash dies als falsch / fehlgeschlagen .
Takatakatek
7

Der einfachste Weg, um bei einem Fehler zu scheitern, ist die Verwendung der Bash- -eOption ( set -eoder /bin/bash -eim Shebang). Dies macht es jedoch nicht einfach, benutzerdefinierte Fehlermeldungen zu senden. Es gibt einige Redewendungen, die es einfacher machen könnten:

die à la Perl

Perl hat einen sehr praktischen dieBefehl, der eine Nachricht an stderr druckt und eine Ausnahme auslöst, die normalerweise zum Beenden des Skripts führt. Sie können dies emulieren:

die () {
  ret=$?
  print "%s\n" "$@" >&2
  exit "$ret"
}

false || die "Obvious error because its false on left"
true || die "This shouldn't print"

trap ERR

Der trap <command>Befehl kann verwendet werden, um einen Befehl auszuführen, wenn ein Signal empfangen wird, wenn das Skript beendet wird ( trap ... EXIT) oder wenn ein Befehl einen Fehler zurückgibt ( trap ... ERR). Also so etwas wie:

trap 'ret=$?; printf "%s\n" "$ERR_MSG" >&2; exit "$ret"' ERR

Dann:

ERR_MSG="Obvious error because its false here"
false
ERR_MSG="This shouldn't print"
true

In beiden Fällen würde ich vorschlagen, mindestens zu verwenden exit 1, um anzuzeigen, dass das Skript fehlgeschlagen ist . Eine Ebene exitverwendet den Exit-Status des vorherigen Befehls. In diesen Fällen handelt es sich um die Befehle echooder printf, die normalerweise erfolgreich sind. Es wäre schön, den Exit-Status des fehlgeschlagenen Befehls zu speichern, wie ich es hier getan habe.

muru
quelle
6

Sie könnten versuchen, ein wenig mit dem Starten des Skripts zu experimentieren

#!/bin/bash -e

Das -e stellt sicher, dass das Skript beendet wird, sobald etwas falsch zurückgegeben wird. Ein bestimmter Fehler kann dann mit vermieden werdensomething || true

aggregat1166877
quelle