Wiele przykładów trap
użycia trap ... INT TERM EXIT
do zadań czyszczenia. Ale czy naprawdę trzeba wymienić wszystkie trzy sigspecy?
Instrukcja mówi:
Jeśli SIGNAL_SPEC to EXIT (0), ARG jest wykonywane przy wyjściu z powłoki.
które moim zdaniem ma zastosowanie niezależnie od tego, czy skrypt zakończył się normalnie, czy zakończył się, ponieważ otrzymał SIGINT
lub SIGTERM
. Eksperyment potwierdza również moje przekonanie:
$ cat ./trap-exit
#!/bin/bash
trap 'echo TRAP' EXIT
sleep 3
$ ./trap-exit & sleep 1; kill -INT %1
[1] 759
TRAP
[1]+ Interrupt ./trap-exit
$ ./trap-exit & sleep 1; kill -TERM %1
[1] 773
TRAP
[1]+ Terminated ./trap-exit
Dlaczego więc tak wiele przykładów wymienia wszystkie z nich INT TERM EXIT
? A może coś mi umknęło i czy jest jakikolwiek przypadek, w którym EXIT
brakowałaby podeszwa ?
INT TERM EXIT
kod czyszczenia jest wykonywany dwukrotnie, gdySIGTERM
lubSIGINT
jest odbierany.Odpowiedzi:
Specyfikacja POSIX nie mówi wiele o warunkach powodujących wykonanie pułapki EXIT, a jedynie o tym, jak musi wyglądać jej środowisko podczas wykonywania.
W skorupie popiołu Busybox test wyjścia z pułapki nie echa „TRAP” przed wyjściem z powodu SIGINT lub SIGTERM. Podejrzewam, że istnieją inne pociski, które również mogą nie działać w ten sposób.
źródło
dash
również nie pułapkuje,EXIT
gdy tylko odbierzeSIGINT/SIGTERM
.zsh
również - być możebash
jest to jedyna powłoka, w którejEXIT
również dopasowuje sygnały.zsh
nie łapie pułapki,EXIT
gdy odbieraINT
, ale robi to, gdy odbieraTERM
. EDYCJA: Właśnie zauważyłem, ile to miało lat ...Tak, jest różnica.
Ten skrypt zakończy działanie po naciśnięciu Enter, wysłaniu
SIGINT
lubSIGTERM
:Ten skrypt zakończy działanie po naciśnięciu Enter:
* Testowane w sh , Bash i Zsh . (nie działa już w sh po dodaniu polecenia uruchomienia pułapki)
Jest też to, co powiedział @Shawn: Ash i Dash nie chwytają sygnałów
EXIT
.Aby więc solidnie obsługiwać sygnały, najlepiej unikać pułapkowania
EXIT
i użyć czegoś takiego:źródło
mktemp
wywołaniami.exit
konieczne wcleanup
?ERR
sobie z tym poradzić, ale nie jest to przenośne .trap - INT TERM; kill -2 $$
jako ostatni wiersz czyszczenia, aby powiedzieć powłoce nadrzędnej, że zakończyła przedwcześnie. Jeśli powłoka nadrzędna foobar.sh wywołuje twój skrypt (foo.sh), a następnie wywołuje bar.sh, nie chcesz, aby bar.sh wykonał się, jeśli INT / TERM zostanie wysłany do twojego foo.sh.trap cleanup EXIT
zajmie się tą propagacją automatycznie, więc jest to najbardziej niezawodna funkcja IMO. Oznacza to również, że nie będziesz musiał dzwonićcleanup
na końcu skryptu.Udoskonalenie ostatniej odpowiedzi, ponieważ ma problemy:
Punkty powyżej:
Programy obsługi INT i TERM nie zamykają się dla mnie podczas testowania - obsługują błąd, a następnie powłoka powraca do wyjścia (i nie jest to zbyt zaskakujące). Zapewniam więc, że czyszczenie kończy się później, aw przypadku sygnałów zawsze używa kodu błędu (aw innym przypadku normalnego wyjścia zachowuje kod błędu).
W przypadku bash wydaje się, że wychodzenie z modułu obsługi INT wywołuje również moduł obsługi EXIT, dlatego odwijam moduł obsługi wyjścia i sam go nazywam (który będzie działał w dowolnej powłoce niezależnie od zachowania).
Wyłapuję wyjście, ponieważ skrypty powłoki mogą wyjść, zanim osiągną dno - błędy składniowe, zestaw -e i niezerowy powrót, po prostu wywołując exit. Nie możesz polegać na tym, że skrypt-skrypt dojdzie do sedna.
SIGQUIT to Ctrl- \ jeśli nigdy tego nie próbowałeś. Daje ci bonusowy rdzeń. Więc myślę, że warto również złapać w pułapkę, nawet jeśli jest to trochę niejasne.
Doświadczenie z przeszłości mówi, że jeśli (podobnie jak ja) zawsze naciskasz Ctrl-C kilka razy, czasami złapiesz go w połowie procedury czyszczenia skryptu powłoki, więc to działa, ale nie zawsze tak idealnie, jak chcesz.
źródło
trap
rozmówcy dostanie 130 dla SIGINT, 143 dla SIGTERM itp Więc chciałbym uchwycić i przekazać poprawny kod wyjścia jak:sig_cleanup() { err=$?; trap '' EXIT; (exit $err); cleanup; }
.trap '' EXIT INT TERM
funkcji czyszczenia? Czy ma to zapobiec przypadkowemu zakłóceniu czyszczenia przez użytkownika, o którym wspomniałeś w ostatnim akapicie? Czy to nie jestEXIT
zbędne?