Co jest równoważne z && podczas pisania skryptu bash?

31

Z góry przepraszam, jeśli jest to duplikat pytania. Przed zapytaniem tutaj postarałem się wyszukać / sprawdzić.

Nie mam nic przeciwko pisaniu takich linijek:

foocommand && foocommand2 && foocommand3

Chodzi o to, że chcę, aby kolejne polecenia były uruchamiane tylko wtedy, gdy poprzednie było „udane”.

Piszę dość długi skrypt, a ten jednowierszowy program jest niewykonalny, ponieważ dla wszystkich innych wygląda na ogromny blok mylącego kodu.

Chcę rozdzielić polecenia i napisać między nimi komentarze w skrypcie. Jak mogę to zrobić i nadal mieć odpowiednik &&?

Mike B.
źródło
4
Te powtarzające niedźwiedzie: &&nie znaczy późniejsza komenda będzie działać, jeśli poprzednie jeden był udany. Oznacza to polecenie będzie działać, jeśli zbiorowy wynik wśród wszystkich poprzednich poleceń na liście poleceń jest sukcesem. Być może wiesz o tym, ale przyszli czytelnicy mogą źle zrozumieć.
kojiro
1
Również dla przypomnienia opisywane zachowanie, które pochodzi ||i &&(w przeciwieństwie do |lub &) nazywa się zwarciem. Zachowanie stosowane w przypadku tych ostatnich operatorów nazywa się chętną oceną.
AndyPerfect
7
@kojiro: Nie widzę różnicy. a && b && cbędzie działać tylko, bjeśli się apowiedzie, więc „działa tylko, cjeśli się bpowiedzie” i „działa tylko, cjeśli ai boba się uda” są równoważnymi instrukcjami: bnie można odnieść sukcesu, chyba że się apowiedzie.
ruakh
1
@ruakh a || b && cjest bardziej ilustracyjny.
kojiro
8
@ruakh nie, true || false && echo hiwyświetli hi. Specyfikacja brzmi: Operatory „&&” i „||” będą miały równe pierwszeństwo i będą oceniane z pozostawionym skojarzeniem.
kojiro

Odpowiedzi:

23

Możesz to zrobić w następujący sposób:

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

Mam nadzieję, że zrozumiałem, o co prosiłeś

schaiba
źródło
Dzięki. To jest najbliższe temu, co miałem nadzieję zrobić. W razie potrzeby bardzo łatwo przekształca się z powrotem w jedną wkładkę.
Mike B
Mike, ponieważ obawiasz się o czystość kodu, powszechną konwencją jest wcięcie 2-N sekcji łańcucha pod pierwszą.
jblaine
@jblaine Nie jestem pewien, czy rozumiem, co masz na myśli. Czy możesz proszę opracować?
Mike B
Mike, Stack Exchange Network nie pozwoli mi sformatować moją odpowiedź prawidłowo, więc o to wszystko, co oznaczało: tiret
jblaine
@jblaine Dobry punkt, edytowany w celu wcięcia linii.
Volker Siegel,
38

Możesz zmienić linię shebang na

#!/bin/bash -e

Po każdym błędzie skrypt przestanie działać.

choroba
źródło
1
Ciekawy. Zapamiętam to. Dziękuję Ci.
Mike B
ratuje mnie przed pisaniem zestawu -e, gdy go potrzebuję
Silverrocker
@Silverrocker To także POSIX (z wyjątkiem bashczęści oczywiście). Powłoka POSIX ma wiele opcji, które można zastosować set. Lub odwrotnie? setto sposób ustawiania opcji wiersza poleceń powłoki w skrypcie.
Kaz
7
Niestety istnieje wiele standardowych narzędzi, które nie są zgodne ze zwykłymi konwencjami dotyczącymi statusu wyjścia, więc -emogą się źle zachowywać. Na przykład prawdopodobnie nie chcesz, aby skrypt kończył się za każdym razem, gdy diffdwa nieidentyczne pliki. Możesz oczywiście obejść ten problem, dodając || true(lub być może || [ $? = 1 ]) do takich poleceń, ale OP wspomina o „wszystkich innych”, którzy będą musieli przeczytać ten skrypt, i powiedział, że „wszyscy inni” prawdopodobnie nie będą przyzwyczajeni do konieczności poradzić sobie z -e.
ruakh
4
Kiedyś -ewłączałem ją, dopóki jeden z administratorów mojej firmy nie uruchamiał moich skryptów bash myscript.sh, więc to zignorowało -e. Teraz wszystkie moje skrypty mają set -edrugą linię.
Carlos Campderrós
19

Jeśli nie podoba ci się ten set -epomysł, być może możesz odwrócić logikę.

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

Bardziej użyteczne, zamień na exitcoś, aby wydrukować przydatny komunikat o błędzie, a następnie wyjdź. Oczywiście wewnątrz funkcji chcesz returnzamiast exit.

potrójny
źródło
11
Lub foocommand || exitwyjść ze foocommandstatusem wyjścia, jeśli niezerowy.
Stéphane Chazelas
Jak to działa? Czy to tak, jak w C - jeśli lewy argument ma wartość PRAWDA, nie jest już sprawdzany żaden argument?
Vorac
Tak to prawda. Jest to powszechny idiom w Lisp (i, jak sądzę, ogólnie w językach funkcjonalnych) oraz w Perlu.
tripleee
9

Zamiast tego możesz użyć if else fibloków.

if foocommand; then

  # some comments

  if foocommand2; then

    # more comments

    foocommand3
  fi
fi

Jest trochę bardziej czytelny.

Alternatywnie możesz po prostu użyć, \aby rozbić swój duży 1 liniowiec na kilka linii

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

Ale może to być mylące dla niewprawnego oka.

Martín Canaval
źródło
4
Nie sądzę, żeby było \to konieczne.
Samuel Edwin Ward
Masz rację. Siła nawyku.
Martín Canaval
5

Możesz rozważyć użycie ifinstrukcji zagnieżdżonych . Inną opcją jest użycie curly's do grupowania jak{ true && echo hi; } || echo huh

Aktualizacja 1:

Oto przykład bez nowych linii / komentarzy:

{ { false && echo "inner true"; } && { echo "inner true" && true; } || { echo "inner false" && false; } || echo "outter false"; }
livingstaccato
źródło
@Stephane Dziękujemy za dodanie średnika. Korzystam z telefonu, by odpowiadać, więc nie sprawdziłem dwukrotnie moich odpowiedzi. :)
livingstaccato,
Ciekawy pomysł. Tak, myślałem o zagnieżdżonych instrukcjach if, ale jeśli połączę ze sobą wiele rzeczy, może być bałagan.
Mike B
4

Podziel każdą sekwencję poleceń na funkcje:

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

big_block_1 && big_block_2 && big_block_3
Fuad Saud
źródło
0

Zawsze lubię pisać funkcję „fail”, która pozwala mi określić, co zamierzam zrobić, kiedy wystąpiła awaria, a następnie użyć ||wzorca. Podobnie jak poniżej:

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

foocommand || fail "couldn't foo"
Dave
źródło