Jak zatrzymać skrypt bash, gdy warunek się nie powiedzie?

11

Tutaj pokazano użycie ||iw &&jednym wierszu do łączenia wykonywania poleceń: Jak mogę sprawdzić błędy apt-get w skrypcie bash?

Próbuję zatrzymać wykonywanie skryptu, jeśli określony warunek się nie powiedzie,

na przykład

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

Tutaj drukuje Obvious error because its false on the lefti wychodzi z konsoli, czego chciałem.

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

Tutaj nie ma drukowania echa, ale exitpolecenie również się uruchamia, tzn. Konsola jest zamknięta, czy nie powinno się uruchomić exitpolecenia, ponieważ polecenie echa po prawej nie zostało wykonane? Czy też domyślnie stwierdzenie jest uważane za fałszywe po lewej stronie &&operatora?

Edycja: Powinienem był o tym wspomnieć, moim celem było powtórzenie błędu i wyjście, jeśli nie było jasne. wrt do mojego szczególnego przypadku wyłapywania błędów podczas grupowania warunków za pomocą && i ||, @ bodhi.zazen odpowiedź rozwiązuje problem.

Odpowiedź @takatakatek wyjaśnia lepiej kontrolę przepływu, a linki prowadzące bash są doskonałe

Odpowiedź @muru ma dobre wyjaśnienie, dlaczego nie używać zestawu -e, jeśli chcesz wyświetlać niestandardowe komunikaty o błędach z alternatywami używania perla i pułapki, które moim zdaniem są bardziej niezawodne i widzę, że używam go od drugiego skryptu bash!

kosol
źródło
O ile mogę powiedzieć, kończy się po zakończeniu skryptu.
Panther

Odpowiedzi:

10

Prawdopodobnie chcesz (jak zauważył Steeldriver)

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

W przeciwnym razie skrypt będzie odczytywał jeden warunek naraz

a lub b, a następnie wyjdź

Myślę, że chcesz a lub (b i wyjść)?

Pantera
źródło
4
W rzeczywistości musisz użyć { ... ; }(jak sugeruje @steeldriver), w przeciwnym razie exittylko wyjdzie z podpowłoki, a powłoka nadrzędna będzie kontynuowała wykonywanie skryptu.
Gordon Davisson,
14

Problem polega na tym, że || i && pomiń tylko jedną kolejną zwrotkę łańcucha poleceń, gdy warunek się nie powiedzie. Jeśli napiszesz pełną strukturę bloków, ma to sens. To, co napisałeś, staje się następujące:

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

To, czego chcesz, to:

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

Kiedy używasz skrótowych operatorów warunkowych Basha, lepiej unikać ich mieszania. Logika jest znacznie łatwiejsza do zrozumienia podczas pisania, a czytelnicy docenią twój dobry styl.

takatakatek
źródło
2
Tak, moim zdaniem jest to o wiele więcej, jak należy napisać warunkowanie w bash
derHugo,
@takatakatek To wyjaśnia, dlaczego w moim przypadku zawodzi. Zgadzam się, że logika jest tutaj jasna, ale nie jest powodem grupowania warunków za pomocą && i || czy należy unikać wyrażania ich za pomocą instrukcji warunkowych?
kosol,
@kosol Tak, && i || połącz dwie rzeczy: Testowanie statusu wyjścia poprzedniego polecenia i określenie pojedynczego polecenia (lub grupy poleceń), które ma zostać wykonane (dla &&) lub pominięte (dla ||). Mogą być przydatne w bardzo prostych przypadkach, ale nie mogą zastąpić w pełni wyrażonych if ... then ... elif ... else ... fibloków. Podejrzewam, że możesz nie wiedzieć, że Bash nie ma prawdziwych typów boolowskich w bardziej wyrafinowanych językach. Jeśli status wyjścia polecenia wynosi 0 (zero), Bash traktuje to jako prawda / sukces . Jeśli status wyjścia jest niezerowy, Bash traktuje to jako fałsz / niepowodzenie .
takatakatek
7

Najprostszym sposobem na niepowodzenie w przypadku błędu jest użycie -eopcji bash ( set -elub /bin/bash -ew shebang). Nie ułatwia to jednak wysyłania niestandardowych komunikatów o błędach. Istnieje kilka idiomów, które mogą uprościć:

die à la Perl

Perl ma bardzo wygodne diepolecenie, które wypisuje komunikat do stderr i zgłasza wyjątek, który zwykle prowadzi do zakończenia skryptu. Możesz to naśladować:

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

trap <command>Komenda może być użyty do uruchomienia polecenia, gdy odbierany jest sygnał, albo przy wychodzeniu (scenariusz trap ... EXIT), lub gdy polecenie zwraca błąd ( trap ... ERR). Więc coś takiego:

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

Następnie:

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

W obu przypadkach sugeruję użycie przynajmniej exit 1, aby wskazać, że skrypt nie powiódł się . Zwykły exitużyje statusu wyjścia poprzedniego polecenia, którym w takich przypadkach byłyby polecenia echolub printf, które zwykle kończyłyby się powodzeniem. Zapisywanie statusu wyjścia nieudanego polecenia, tak jak to zrobiłem tutaj, byłoby przyjemne.

muru
źródło
6

Możesz spróbować poeksperymentować z uruchomieniem skryptu

#!/bin/bash -e

-E zapewnia, że ​​skrypt kończy działanie w momencie, gdy coś zwraca false. Dzięki temu można uniknąć określonej awariisomething || true

aggregate1166877
źródło