Was ist das Äquivalent zu && beim Schreiben eines Bash-Skripts?

31

Ich entschuldige mich im Voraus, wenn dies eine doppelte Frage ist. Ich habe mich bemüht zu suchen / zu überprüfen, bevor ich hier gefragt habe.

Ich schreibe gerne Einzeiler wie folgt:

foocommand && foocommand2 && foocommand3

Die Idee ist, dass nachfolgende Befehle nur ausgeführt werden sollen, wenn der vorherige "erfolgreich" war.

Ich schreibe ein etwas langwieriges Skript und dieser Einzeiler ist nicht machbar, weil er für alle anderen wie ein riesiger Block verwirrenden Codes aussieht.

Ich möchte die Befehle ausräumen und Kommentare dazwischen im Skript schreiben. Wie kann ich das tun und habe trotzdem das Äquivalent von && drin?

Mike B
quelle
4
Diese Bärnwiederholen: &&bedeutet nicht , der nachfolgende Befehl wird ausgeführt , wenn der vorherige ein erfolgreich war. Es bedeutet , dass der Befehl ausgeführt werden soll , wenn das kollektive Ergebnis von all den vorherigen Befehlen in der Befehlsliste ist der Erfolg. Sie wissen das vielleicht, aber zukünftige Leser verstehen das vielleicht falsch.
Kojiro
1
Auch nur als Hinweis, dieses Verhalten, das Sie beschreiben, kommt von ||und wird &&(im Gegensatz zu |oder &) Kurzschluss genannt. Das mit den letzteren Operatoren verwendete Verhalten wird als eifrige Auswertung bezeichnet.
AndyPerfect
7
@kojiro: Ich sehe den Unterschied nicht. a && b && cwird nur ausgeführt, bwenn dies aerfolgreich ist. "Es wird nur ausgeführt, cwenn dies berfolgreich ist" und "Es wird nur ausgeführt, cwenn aund bbeide erfolgreich sind" sind äquivalente Anweisungen: bKann nicht erfolgreich sein, wenn dies nicht aerfolgreich ist.
Ruakh
1
@ruakh a || b && cist anschaulicher.
Kojiro
8
@ruakh nein, true || false && echo hiwird ausgegeben hi. Die Spezifikation lautet, Die Operatoren "&&" und "||" haben die gleiche Priorität und werden mit linker Assoziativität bewertet.
Kojiro

Antworten:

23

Du kannst es so machen:

#!/bin/sh
ls -lh &&
    # This is a comment
    echo 'Wicked, it works!'

Ich hoffe, ich habe verstanden, was Sie richtig gefragt haben.

schaiba
quelle
Vielen Dank. Dies kommt dem, was ich mir erhofft hatte, am nächsten. Und es macht es sehr einfach, ihn bei Bedarf wieder in einen Einzeiler umzuwandeln.
Mike B
Mike, da Sie sich Sorgen um die Code-Sauberkeit machen, ist es eine übliche Konvention, die 2-N-Abschnitte der Kette unter dem ersten einzurücken.
Jblaine
@jblaine Ich bin nicht sicher, ob ich verstehe, was du meinst. Können Sie bitte näher darauf eingehen?
Mike B
Mike, Stackexchange lässt mich meine Antwort nicht richtig formatieren, also ist hier alles, was ich damit gemeint habe: Einzug
jblaine
@jblaine Guter Punkt, bearbeitet, um die Linien einzurücken.
Volker Siegel
38

Sie können die shebang Linie zu ändern

#!/bin/bash -e

Nach einem Fehler wird das Skript beendet.

Choroba
quelle
1
Interessant. Ich werde mir das merken. Vielen Dank.
Mike B
Bewahrt mich
davor,
@Silverrocker Es ist auch POSIX (mit Ausnahme des bashTeils natürlich). Die POSIX-Shell bietet eine Reihe von Optionen, die für zutreffend sind set. Oder umgekehrt? setDies ist eine Möglichkeit, Shell-Befehlszeilenoptionen im Skript festzulegen.
Kaz
7
Leider gibt es eine Reihe von Standarddienstprogrammen, die nicht den üblichen Exit-Status-Konventionen entsprechen und sich daher -emöglicherweise schlecht verhalten. Zum Beispiel möchten Sie wahrscheinlich nicht, dass Ihr Skript jedes Mal beendet wird, wenn Sie diffzwei nicht identische Dateien haben. Sie können das natürlich umgehen, indem Sie solche Befehle anhängen || true(oder vielleicht auch anhängen || [ $? = 1 ]), aber das OP erwähnt "alle anderen", die dieses Skript lesen müssen, und sagt, dass "alle anderen" wahrscheinlich nicht daran gewöhnt sind fertig werden mit -e.
Ruakh
4
Früher habe ich das -eauf "she-bang" gesetzt, bis ein Administrator meiner Firma meine Skripte weiterhin mit startete bash myscript.sh, also ignorierte er das -e. Jetzt haben alle meine Skripte ein set -ein der zweiten Zeile.
Carlos Campderrós
19

Wenn Ihnen die set -eIdee nicht gefällt , können Sie vielleicht die Logik umkehren.

foocommand || exit 1
foocommand2 || exit 2
foocommand3 || exit 3

Ersetzen Sie sie sinnvollerweise durch exitetwas, um eine nützliche Fehlermeldung zu drucken, und beenden Sie sie dann. Innerhalb einer Funktion möchten Sie natürlich returnstatt exit.

Tripleee
quelle
11
Oder foocommand || exitum mit foocommanddem Beendigungsstatus zu beenden, wenn er nicht Null ist.
Stéphane Chazelas
Wie funktioniert das? Ist es wie in C - wenn das linke Argument TRUE ergibt, werden die nicht mehr Argumente geprüft?
Vorac
Ja, das ist richtig. Es ist eine gebräuchliche Redewendung in Lisp (und allgemein in funktionalen Sprachen, nehme ich an) und in Perl.
Tripleee
9

Sie können if else fistattdessen Blöcke verwenden.

if foocommand; then

  # some comments

  if foocommand2; then

    # more comments

    foocommand3
  fi
fi

Es ist etwas lesbarer.

Alternativ können Sie \Ihren Big 1 Liner auch einfach in mehrere Zeilen aufteilen

foocommand && \
  # some comment
  foocommand2 && \
    # more comment
    foocommand3

Aber natürlich kann dies für das ungeübte Auge verwirrend sein.

Martín Canaval
quelle
4
Ich denke nicht, dass das \dort notwendig ist.
Samuel Edwin Ward
Du hast recht. Kraft der Gewohnheit.
Martín Canaval
5

Sie können auch verschachtelte ifAnweisungen verwenden. Eine andere Möglichkeit ist die Verwendung von Curly, um eine Gruppe zu bilden{ true && echo hi; } || echo huh

Update 1:

Hier ist ein Beispiel ohne Zeilenumbrüche / Kommentare:

{ { false && echo "inner true"; } && { echo "inner true" && true; } || { echo "inner false" && false; } || echo "outter false"; }
livingstaccato
quelle
@Stephane Danke für das Hinzufügen von Semikolon. Ich habe mit meinem Telefon geantwortet, also habe ich meine Antworten nicht doppelt überprüft. :)
livingstaccato
Interessante Idee. Ja, ich hatte über geschachtelte if-Anweisungen nachgedacht, aber wenn ich eine Menge Dinge aneinander reihe, kann es chaotisch werden.
Mike B
4

Teilen Sie jede Befehlssequenz in Funktionen auf:

big_block_1() {
  # ...
}
big_block_2() {
  # ...
}
big_block_3() {
  # ...
}

big_block_1 && big_block_2 && big_block_3
Fuad Saud
quelle
0

Ich schreibe immer gerne eine "Fehler" -Funktion, mit der ich angeben kann, was ich tun wollte, als der Fehler auftrat, und dann das ||Muster verwenden. Wie die folgenden:

function fail() {
  echo "Failure: ${@}"
  exit 1
}

foocommand || fail "couldn't foo"
Dave
quelle