Czy istnieje coś podobnego do pipefail dla wielu poleceń, na przykład instrukcja „try”, ale w bash. Chciałbym zrobić coś takiego:
echo "trying stuff"
try {
command1
command2
command3
}
I w dowolnym momencie, jeśli jakieś polecenie zawiedzie, porzuć i powtórz błąd tego polecenia. Nie chcę robić czegoś takiego:
command1
if [ $? -ne 0 ]; then
echo "command1 borked it"
fi
command2
if [ $? -ne 0 ]; then
echo "command2 borked it"
fi
I tak dalej ... lub coś w stylu:
pipefail -o
command1 "arg1" "arg2" | command2 "arg1" "arg2" | command3
Ponieważ uważam, że argumenty każdego polecenia (popraw mnie, jeśli się mylę) będą się wzajemnie zakłócały. Te dwie metody wydają mi się strasznie długie i nieprzyjemne, dlatego apeluję o bardziej wydajną metodę.
set -euo pipefail
.set -e
to okropny pomysł. Zobacz ćwiczenia w BashFAQ # 105 omawiające tylko kilka nieoczekiwanych przypadków brzegowych, które wprowadza, i / lub porównanie pokazujące niezgodności między implementacjami różnych powłok (i wersji powłok) na stronie inulm.de/~mascheck/various/set -e .Odpowiedzi:
Możesz napisać funkcję, która uruchomi i przetestuje polecenie. Załóż
command1
icommand2
są zmiennymi środowiskowymi, które zostały ustawione na polecenie.źródło
$*
, zakończy się niepowodzeniem, jeśli w argumentach będą spacje; użyj"$@"
zamiast tego. Podobnie umieść$1
w cudzysłowie wecho
poleceniu.test
ponieważ jest to wbudowane polecenie.ls
. Jeśli wywołaszls foo
i otrzymasz komunikat o błędzie formularzals: foo: No such file or directory\n
, rozumiesz problem. Jeśli zamiast tegols: foo: No such file or directory\nerror with ls\n
zostaniesz rozproszony przez zbędne informacje. W takim przypadku łatwo jest argumentować, że nadmiar jest trywialny, ale szybko rośnie. Ważne są zwięzłe komunikaty o błędach. Ale co ważniejsze, ten rodzaj opakowania zachęca zbyt pisarzy, aby całkowicie pomijali dobre komunikaty o błędach.Co rozumiesz przez „porzuć i powtórz błąd”? Jeśli masz na myśli, że chcesz, aby skrypt zakończył się natychmiast po niepowodzeniu dowolnego polecenia, po prostu zrób to
na początku skryptu (ale uwaga poniżej). Nie zawracaj sobie głowy wyświetlaniem komunikatu o błędzie: pozwól temu błędnemu poleceniu sobie z tym poradzić. Innymi słowy, jeśli to zrobisz:
i polecenie 2 nie powiedzie się, podczas drukowania komunikatu o błędzie do stderr, wtedy wydaje się, że osiągnąłeś to, co chcesz. (Chyba że źle interpretuję to, czego chcesz!)
Jako następstwo każde napisane polecenie musi zachowywać się dobrze: musi zgłaszać błędy do stderr zamiast do standardowego wyjścia (przykładowy kod w pytaniu drukuje błędy do standardowego wyjścia) i musi zakończyć działanie z niezerowym statusem, gdy się nie powiedzie.
Nie uważam jednak tego za dobrą praktykę.
set -e
zmienił swoją semantykę z różnymi wersjami bash i chociaż działa dobrze w przypadku prostego skryptu, jest tak wiele przypadków krawędzi, że w zasadzie jest on bezużyteczny. (Rozważ takie rzeczy jak:set -e; foo() { false; echo should not print; } ; foo && echo ok
semantyka tutaj jest nieco rozsądna, ale jeśli przekodujesz kod na funkcję, która polegała na ustawieniu opcji wcześniejszego zakończenia, możesz łatwo zostać ugryziony.) IMO lepiej napisać:lub
źródło
trap some_func 0
wykona sięsome_func
przy wyjściu)|| exit
wprost po każdym poleceniu.Mam zestaw funkcji skryptowych, z których często korzystam w systemie Red Hat. Używają funkcji systemowych od
/etc/init.d/functions
do drukowania zielonego[ OK ]
i czerwonego[FAILED]
wskaźników stanu.Możesz opcjonalnie ustawić
$LOG_STEPS
zmienną na nazwę pliku dziennika, jeśli chcesz rejestrować, które polecenia się nie powiodły.Stosowanie
Wynik
Kod
źródło
Co jest warte, krótszy sposób na napisanie kodu, aby sprawdzić, czy każde polecenie się powiodło, to:
Nadal jest nudny, ale przynajmniej czytelny.
źródło
command1 &> /dev/null || echo "command1 borked it"
command1 || (echo command1 borked it ; exit)
Alternatywą jest po prostu łączenie poleceń razem,
&&
aby pierwsza, która uległa awarii, uniemożliwiła wykonanie pozostałej części:To nie jest składnia, o którą prosiłeś w pytaniu, ale jest to powszechny wzorzec opisywanego przypadku użycia. Ogólnie polecenia powinny być odpowiedzialne za awarie drukowania, abyś nie musiał tego robić ręcznie (być może z
-q
flagą, aby wyciszyć błędy, gdy ich nie chcesz). Jeśli masz możliwość modyfikowania tych poleceń, edytowałbym je, aby krzyczeć na niepowodzenie, zamiast zawijać je w coś, co to robi.Zauważ też, że nie musisz robić:
Możesz po prostu powiedzieć:
I kiedy zrobić potrzebę sprawdzenia kody powrotne użyć arytmetyczną kontekst zamiast
[ ... -ne
:źródło
Zamiast tworzyć funkcje runnera lub używać
set -e
, użyjtrap
:Pułapka ma nawet dostęp do numeru linii i wiersza polecenia, które ją uruchomiło. Zmienne to
$BASH_LINENO
i$BASH_COMMAND
.źródło
trap - ERR
aby wyłączyć pułapkę na końcu „bloku”.Osobiście wolę stosować lekkie podejście, jak widać tutaj ;
Przykładowe użycie:
źródło
źródło
$*
, zakończy się niepowodzeniem, jeśli w argumentach będą spacje; użyj"$@"
zamiast tego. (Chociaż polecenie $ * jest w porządkuecho
.)Opracowałem prawie bezbłędną implementację try & catch w bash, która umożliwia pisanie kodu takiego jak:
Możesz nawet zagnieżdżać w sobie bloki try-catch!
Kod jest częścią mojej płyty bazowej / frameworku bash . To dodatkowo rozszerza pomysł try & catch na takie rzeczy jak obsługa błędów z funkcją śledzenia wstecznego i wyjątkami (plus kilka innych fajnych funkcji).
Oto kod odpowiedzialny tylko za try & catch:
Nie krępuj się używać, rozwidlać i dodawać - jest na GitHub .
źródło
Przepraszam, że nie mogę skomentować pierwszej odpowiedzi. Powinieneś jednak użyć nowej instancji, aby wykonać polecenie: cmd_output = $ ($ @)
źródło
Dla użytkowników skorupy ryb, którzy natkną się na ten wątek.
Niech
foo
będzie funkcją, która nie „zwraca” (echo) wartości, ale ustawia kod wyjścia jak zwykle.Aby uniknąć sprawdzania
$status
po wywołaniu funkcji, możesz:A jeśli jest za długi, aby zmieścić się w jednej linii:
źródło
Kiedy używam
ssh
, muszę rozróżnić problemy spowodowane przez problemy z połączeniem i kody błędów polecenia zdalnego w trybieerrexit
(set -e
). Korzystam z następującej funkcji:źródło
Możesz użyć niesamowitego rozwiązania @ john-kugelman znalezionego powyżej w systemach innych niż RedHat, komentując ten wiersz w jego kodzie:
Następnie wklej poniższy kod na końcu. Pełne ujawnienie: Jest to tylko bezpośrednia kopia i wklejenie odpowiednich fragmentów wyżej wspomnianego pliku pobranego z Centos 7.
Testowane na MacOS i Ubuntu 18.04.
źródło
Sprawdzanie statusu w sposób funkcjonalny
Stosowanie:
Wynik
źródło