Jaka jest twoja ulubiona metoda radzenia sobie z błędami w Bash? Najlepszy przykład obsługi błędów, który znalazłem w Internecie, napisał William Shotts, Jr, na stronie http://www.linuxcommand.org .
Sugeruje użycie następującej funkcji do obsługi błędów w Bash:
#!/bin/bash
# A slicker error handling routine
# I put a variable in my scripts named PROGNAME which
# holds the name of the program being run. You can get this
# value from the first item on the command line ($0).
# Reference: This was copied from <http://www.linuxcommand.org/wss0150.php>
PROGNAME=$(basename $0)
function error_exit
{
# ----------------------------------------------------------------
# Function for exit due to fatal program error
# Accepts 1 argument:
# string containing descriptive error message
# ----------------------------------------------------------------
echo "${PROGNAME}: ${1:-"Unknown Error"}" 1>&2
exit 1
}
# Example call of the error_exit function. Note the inclusion
# of the LINENO environment variable. It contains the current
# line number.
echo "Example of error with line number and message"
error_exit "$LINENO: An error has occurred."
Czy masz lepszą procedurę obsługi błędów używaną w skryptach Bash?
Odpowiedzi:
Użyj pułapki!
... wtedy, gdy utworzysz plik tymczasowy:
i
$temp_foo
zostaną usunięte przy wyjściu, a bieżący numer linii zostanie wydrukowany. (set -e
zapewni również zachowanie wyjścia po błędzie, chociaż wiąże się to z poważnymi zastrzeżeniami i osłabia przewidywalność i przenośność kodu).Możesz albo pozwolić pułapce
error
na ciebie zadzwonić (w takim przypadku używa domyślnego kodu wyjścia 1 i bez komunikatu) lub zadzwonić do siebie i podać wyraźne wartości; na przykład:wyjdzie ze statusem 2 i da wyraźny komunikat.
źródło
To dobre rozwiązanie. Chciałem tylko dodać
jako podstawowy mechanizm błędu. Natychmiast zatrzyma skrypt, jeśli proste polecenie zawiedzie. Myślę, że powinno to być zachowanie domyślne: ponieważ takie błędy prawie zawsze oznaczają coś nieoczekiwanego, wykonywanie następujących poleceń nie jest tak naprawdę rozsądne.
źródło
set -e
nie jest bez gotchas: zobacz mywiki.wooledge.org/BashFAQ/105 dla kilku.set -o pipefail
set -e
ma wysoki stosunek korzyści do kosztów.set -e
siebie, ale wielu innych stałych bywalców w irc.freenode.org # bash odradza (dość silnie) przeciw temu. Przynajmniej te problemy powinny być dobrze zrozumiane.Czytanie wszystkich odpowiedzi na tej stronie bardzo mnie zainspirowało.
Oto moja wskazówka:
zawartość pliku: lib.trap.sh
Przykład użycia:
zawartość pliku: trap-test.sh
Bieganie:
Wynik:
Jak widać na poniższym zrzucie ekranu, dane wyjściowe są kolorowe, a komunikat błędu pojawia się w używanym języku.
źródło
test ${#g_libs[@]} == 0
nie jest zgodny z POSIX (test POSIX obsługuje=
porównania ciągów lub porównania-eq
numeryczne, ale nie==
wspominając o braku tablic w POSIX), a jeśli nie próbujesz być zgodny z POSIX, dlaczego w czytest
w ogóle używasz świata, a nie kontekstu matematycznego?(( ${#g_libs[@]} == 0 ))
jest przecież łatwiejszy do odczytania.case "$(uname)" in Darwin ) stderr_log="${TMPDIR}stderr.log";; Linux ) stderr_log="/dev/shm/stderr.log";; * ) stderr_log="/dev/shm/stderr.log" ;; esac
Równoważną alternatywą dla „zestawu -e” jest
To sprawia, że znaczenie flagi jest bardziej wyraźne niż tylko „-e”.
Losowe dodawanie: aby tymczasowo wyłączyć flagę i powrócić do ustawień domyślnych (kontynuowanie wykonywania niezależnie od kodów wyjścia), wystarczy użyć
Wyklucza to prawidłową obsługę błędów wspomnianą w innych odpowiedziach, ale jest szybkie i skuteczne (podobnie jak bash).
źródło
$(foo)
na gołej linii, a nie tylko,foo
jest zwykle błędną rzeczą. Po co go promować, podając go jako przykład?Zainspirowany przedstawionymi tutaj pomysłami, opracowałem czytelny i wygodny sposób radzenia sobie z błędami w skryptach bash w moim bashowym projekcie .
Po prostu pozyskując bibliotekę, otrzymujesz następujące rzeczy po wyjęciu z pudełka (tj. Zatrzyma to wykonywanie każdego błędu, jakbyś używał
set -e
dziękitrap
onERR
i trochę bash-fu ):Istnieje kilka dodatkowych funkcji, które pomagają radzić sobie z błędami, takich jak try-catch lub słowo kluczowe throw , które pozwalają przerwać wykonywanie w punkcie, aby zobaczyć ślad. Dodatkowo, jeśli terminal go obsługuje, wyrzuca emoji z linii energetycznej, koloryzuje części danych wyjściowych w celu zapewnienia dużej czytelności i podkreśla metodę, która spowodowała wyjątek w kontekście wiersza kodu.
Minusem jest to, że - nie jest przenośny - kod działa w trybie bash, prawdopodobnie> = 4 (ale wyobrażam sobie, że można go przenieść z pewnym wysiłkiem w celu bash 3).
Kod jest podzielony na wiele plików dla lepszej obsługi, ale zainspirował mnie pomysł śledzenia wstecznego z powyższej odpowiedzi Luca Borrione .
Aby przeczytać więcej lub spojrzeć na źródło, zobacz GitHub:
https://github.com/niieani/bash-oo-framework#error-handling-with-exceptions-and-throw
źródło
Wolę coś naprawdę łatwego do połączenia. Używam więc czegoś, co wygląda trochę skomplikowane, ale jest łatwe w użyciu. Zwykle po prostu kopiuję i wklejam poniższy kod do swoich skryptów. Wyjaśnienie następuje po kodzie.
Zwykle wywołuję funkcję czyszczenia po stronie funkcji error_exit, ale różni się ona w zależności od skryptu, więc ją pominąłem. Pułapki wychwytują typowe sygnały kończące i upewniają się, że wszystko zostanie wyczyszczone. Alias to prawdziwa magia. Lubię sprawdzać wszystko pod kątem awarii. Ogólnie rzecz biorąc, programy nazywam „jeśli!” instrukcja typu. Odejmując 1 od numeru linii, alias powie mi, gdzie wystąpiła awaria. Można również zadzwonić i jest to idiota. Poniżej znajduje się przykład (wystarczy zastąpić / bin / false tym, co chcesz wywołać).
źródło
$LINENO - 1
. Pokaż poprawnie bez tego.false || die "hello death"
Innym zagadnieniem jest kod wyjścia do zwrócenia. Po prostu „
1
” jest dość standardem, chociaż istnieje garść zarezerwowanych kodów wyjścia, których używa bash , a ta sama strona twierdzi, że kody zdefiniowane przez użytkownika powinny być w zakresie 64-113, aby były zgodne ze standardami C / C ++.Możesz również rozważyć podejście oparte na wektorze bitowym, które
mount
wykorzystuje swoje kody wyjścia:OR
-synchronizowanie kodów razem pozwala twojemu skryptowi zasygnalizować wiele jednoczesnych błędów.źródło
Używam następującego kodu pułapki, pozwala także na śledzenie błędów poprzez potoki i polecenia „time”
źródło
function
Słowo kluczowe jest bezinteresownie POSIX-niekompatybilne. Zastanów się nad tym, aby złożyć oświadczenie sprawiedliwieerror() {
, bezfunction
wcześniejszego.${$?}
powinno być$?
, lub${?}
jeśli nalegasz na użycie niepotrzebnych aparatów ortodontycznych; wnętrze$
jest złe.Użyłem
przed; myślę, że „wyjście” z jakiegoś powodu zawiodło mnie. Powyższe wartości domyślne wydają się jednak dobrym pomysłem.
źródło
To służył mi dobrze przez jakiś czas teraz. Drukuje komunikaty o błędach lub ostrzeżenia na czerwono, jeden wiersz na parametr i umożliwia opcjonalny kod wyjścia.
źródło
Nie jestem pewien, czy to ci pomoże, ale zmodyfikowałem tutaj niektóre z sugerowanych funkcji, aby uwzględnić w nim sprawdzenie błędu (kod wyjścia z poprzedniego polecenia). Przy każdym „sprawdzaniu” przekazuję również jako parametr „komunikat” o błędzie do celów logowania.
Teraz, aby wywołać go w tym samym skrypcie (lub w innym, jeśli go używam
export -f error_exit
), po prostu piszę nazwę funkcji i przekazuję komunikat jako parametr, jak poniżej:Dzięki temu udało mi się stworzyć naprawdę solidny plik bash dla niektórych zautomatyzowanych procesów, który zatrzyma się w przypadku błędów i powiadomi mnie (
log.sh
zrobi to)źródło
function
słowa kluczowego, tylkoerror_exit() {
.cd /home/myuser/afolder || error_exit "Unable to switch to folder"
?Ta sztuczka jest przydatna w przypadku brakujących poleceń lub funkcji. Nazwa brakującej funkcji (lub pliku wykonywalnego) zostanie przekazana w $ _
źródło
$_
byłby dostępny w funkcji tak samo jak$?
? Nie jestem pewien, czy istnieje jakikolwiek powód, aby używać jednego w funkcji, ale nie drugiego.Ta funkcja służy mi ostatnio całkiem dobrze:
Wywołujesz go, dodając 0 lub ostatnią zwracaną wartość do nazwy polecenia do uruchomienia, dzięki czemu możesz łączyć polecenia bez konieczności sprawdzania wartości błędów. Dzięki temu ten blok instrukcji:
Staje się to:
Jeśli którekolwiek z poleceń nie powiedzie się, kod błędu jest po prostu przekazywany na koniec bloku. Uważam, że jest to użyteczne, gdy nie chcesz, aby kolejne polecenia były wykonywane, jeśli poprzednie nie powiodło się, ale nie chcesz też, aby skrypt natychmiast zakończył działanie (na przykład w pętli).
źródło
Korzystanie z pułapki nie zawsze jest opcją. Na przykład, jeśli piszesz jakąś funkcję wielokrotnego użytku, która wymaga obsługi błędów i którą można wywołać z dowolnego skryptu (po uzyskaniu pliku z funkcjami pomocniczymi), funkcja ta nie może zakładać niczego o czasie wyjścia zewnętrznego skryptu, co bardzo utrudnia korzystanie z pułapek. Kolejną wadą korzystania z pułapek jest zła kompozycja, ponieważ istnieje ryzyko zastąpienia poprzedniej pułapki, która może zostać wcześniej ustawiona w łańcuchu dzwoniącego.
Istnieje mała sztuczka, której można użyć do prawidłowej obsługi błędów bez pułapek. Jak zapewne wiesz z innych odpowiedzi,
set -e
nie działa wewnątrz poleceń, jeśli używasz||
operatora po nich, nawet jeśli uruchamiasz je w podpowłoce; np. to nie zadziała:Ale
||
operator jest potrzebny, aby zapobiec powrotowi z funkcji zewnętrznej przed czyszczeniem. Sztuką jest uruchomienie wewnętrznego polecenia w tle, a następnie natychmiast na niego poczekać.wait
Wbudowane zwróci kod wyjścia polecenia wewnętrznej, a teraz używasz||
powait
, a nie funkcji wewnętrznej, więcset -e
działa prawidłowo wewnątrz tego ostatniego:Oto ogólna funkcja oparta na tym pomyśle. Powinien on działać we wszystkich powłokach zgodnych z POSIX, jeśli usuniesz
local
słowa kluczowe, tzn. Zastąpiszlocal x=y
je tylkox=y
:Przykład użycia:
Uruchamianie przykładu:
Jedyną rzeczą, o której musisz wiedzieć, korzystając z tej metody, jest to, że wszystkie modyfikacje zmiennych Shell wykonane z komendy, którą przekazujesz
run
, nie będą propagowane do funkcji wywołującej, ponieważ komenda działa w podpowłoce.źródło