Szukam sposobu na uporządkowanie bałaganu, gdy mój skrypt najwyższego poziomu kończy działanie.
Zwłaszcza, jeśli chcę użyć set -e
, chciałbym, aby proces w tle umarł po zakończeniu skryptu.
Aby posprzątać trochę bałaganu, trap
można użyć. Może dostarczyć listę rzeczy wykonanych, gdy nadejdzie określony sygnał:
trap "echo hello" SIGINT
ale może być również użyty do wykonania czegoś, jeśli powłoka wyjdzie:
trap "killall background" EXIT
Jest wbudowany, więc help trap
poda informacje (działa z bash). Jeśli chcesz tylko zabijać zadania w tle, możesz to zrobić
trap 'kill $(jobs -p)' EXIT
Uważaj, aby użyć pojedynczego '
, aby zapobiec $()
natychmiastowemu podstawieniu powłoki .
kill $(jobs -p)
nie działa w desce rozdzielczej, ponieważ wykonuje podstawianie poleceń w podpowłoce (zobacz Zastępowanie poleceń w myślniku)killall background
ma być zastępczy?background
nie ma na stronie podręcznika ...Działa to dla mnie (poprawione dzięki komentatorom):
kill -- -$$
wysyła SIGTERM do całej grupy procesów, zabijając w ten sposób również potomków.Określenie sygnału
EXIT
jest przydatne podczas używaniaset -e
(więcej szczegółów tutaj ).źródło
4.3.30(1)-release
OSX i jest to potwierdzone również na Ubuntu . Jest jednak obvoius wokaround :)-$$
. Zwraca wartość do „- <PID>„ np-1234
. Na wbudowanej stronie zabijania // wiodący myślnik określa sygnał do wysłania. Jednak - prawdopodobnie blokuje to, ale w przeciwnym razie wiodący myślnik nie jest udokumentowany. Jakaś pomoc?man 2 kill
, co wyjaśnia, że gdy PID jest ujemny, sygnał jest wysyłany do wszystkich procesów w grupie procesów o podanym identyfikatorze ( en.wikipedia.org/wiki/Process_group ). To mylące, że nie jest to wymienione wman 1 kill
lubman bash
, i może być uznane za błąd w dokumentacji.Aktualizacja: https://stackoverflow.com/a/53714583/302079 poprawia to, dodając status wyjścia i funkcję czyszczenia.
Po co konwertować
INT
iTERM
wyjść? Ponieważ oba powinny wyzwalaćkill 0
nie wchodząc w nieskończoną pętlę.Dlaczego spust
kill 0
naEXIT
? Ponieważ normalne wyjścia ze skryptów również powinny się uruchamiaćkill 0
.Dlaczego
kill 0
? Ponieważ zagnieżdżone podpowłoki również muszą zostać zabite. Spowoduje to usunięcie całego drzewa procesów .źródło
kill 0
oznacza / robi?Dokonałbym tylko drobnych zmian w odpowiedzi Johannesa i używał zadania -pr, aby ograniczyć zabijanie do uruchomionych procesów i dodać kilka dodatkowych sygnałów do listy:
źródło
trap 'kill 0' SIGINT SIGTERM EXIT
Rozwiązanie opisane w użytkownika @ tokland odpowiedź jest naprawdę ładne, ale ostatni Bash awarii z winy segmentacja podczas korzystania z niego. Jest tak, ponieważ Bash, począwszy od wersji 4.3, pozwala na rekurencję pułapki, która w tym przypadku staje się nieskończona:SIGINT
lubSIGTERM
lubEXIT
;kill 0
, co wysyłaSIGTERM
do wszystkich procesów w grupie, w tym do samej powłoki;Można to obejść, ręcznie wyrejestrowując pułapkę:
Bardziej wymyślny sposób, który pozwala wydrukować otrzymany sygnał i pozwala uniknąć komunikatów „Zakończone”:
UPD : dodano minimalny przykład; ulepszona
stop
funkcja aviod-pułapkowania niepotrzebnych sygnałów i ukrywania komunikatów „Terminated:” na wyjściu. Dzięki Trevor Boyd Smith za sugestie!źródło
stop()
podać pierwszy argument jako numer sygnału ale wtedy hardcode jakie sygnały są wyrejestrowane. zamiast na stałe wyrejestrowywać sygnały, możesz użyć pierwszego argumentu do wyrejestrowaniastop()
funkcji (może to potencjalnie zatrzymać inne sygnały rekurencyjne (inne niż 3 zakodowane na stałe)).SIGINT
, alekill 0
wysyłaSIGTERM
, co ponownie zostanie uwięzione. Nie spowoduje to jednak nieskończonej rekurencji, ponieważSIGTERM
podczas drugiegostop
połączenia zostanie zablokowany .trap - $1 && kill -s $1 0
powinien działać lepiej. Przetestuję i zaktualizuję tę odpowiedź. Dziękuję za fajny pomysł! :)trap - $1 && kill -s $1 0
też nie zadziała, bo nie możemy zabijaćEXIT
. Ale to naprawdę wystarczy zrobić pułapkęTERM
, ponieważkill
domyślnie wysyła ten sygnał.EXIT
,trap
procedura obsługi sygnału jest zawsze wykonywana tylko raz.Dla pewności lepiej jest zdefiniować funkcję czyszczenia i wywołać ją z pułapki:
lub całkowicie unikając funkcji:
Czemu? Ponieważ po prostu używając
trap 'kill $(jobs -pr)' [...]
jednego zakłada się, że będą uruchomione zadania w tle, gdy zasygnalizowany zostanie stan pułapki. Gdy nie ma żadnych zadań, zobaczysz następujący (lub podobny) komunikat:ponieważ
jobs -pr
jest pusty - skończyłem w tej „pułapce” (gra słów zamierzona).źródło
[ -n "$(jobs -pr)" ]
nie działa na moim bashu. Używam GNU bash, wersja 4.2.46 (2) -release (x86_64-redhat-linux-gnu). Pojawia się komunikat „zabij: użycie”.jobs -pr
nie zwraca PID dzieci procesów w tle. Nie rozrywa całego drzewa procesu, jedynie przycina korzenie.Ładna wersja, która działa pod Linuksem, BSD i MacOS X. Najpierw próbuje wysłać SIGTERM, a jeśli to się nie powiedzie, zabija proces po 10 sekundach.
Pamiętaj, że zadania nie obejmują procesów grand dzieci.
źródło
Jak https://stackoverflow.com/a/22644006/10082476 , ale z dodanym kodem wyjścia
źródło
exit_code
bierzeINT TERM
pułapka?Inną opcją jest ustawienie skryptu jako lidera grupy procesów i wychwycenie killpg w grupie procesów przy wyjściu.
źródło
Więc skrypt ładuje skrypt. Uruchom
killall
komendę (lub inną dostępną w systemie operacyjnym), która zostanie wykonana natychmiast po zakończeniu skryptu.źródło
zadania -p nie działa we wszystkich powłokach, jeśli są wywoływane w podpowłoce, chyba że jego dane wyjściowe zostaną przekierowane do pliku, ale nie do potoku. (Zakładam, że pierwotnie był przeznaczony wyłącznie do użytku interaktywnego).
Co z następującymi kwestiami:
Wezwanie do „zadań” jest potrzebne w powłoce Debiana, która nie aktualizuje bieżącego zadania („%%”), jeśli go brakuje.
źródło
trap 'echo in trap; set -x; trap - TERM EXIT; while kill %% 2>/dev/null; do jobs > /dev/null; done; set +x' INT TERM EXIT; sleep 100 & while true; do printf .; sleep 1; done
Jeśli uruchomisz go w Bash (5.0.3) i spróbujesz zakończyć, wydaje się, że istnieje nieskończona pętla. Jeśli jednak zakończysz to ponownie, zadziała. Nawet przez Dash (0.5.10.2-6) musisz go dwukrotnie zakończyć.Dokonałem adaptacji odpowiedzi @ tokland w połączeniu z wiedzą z http://veithen.github.io/2014/11/16/sigterm-propagation.html, gdy zauważyłem, że
trap
nie uruchamia się, jeśli uruchomię proces pierwszego planu (bez tła&
):Przykład jego działania:
źródło
Dla urozmaicenia opublikuję odmianę https://stackoverflow.com/a/2173421/102484 , ponieważ to rozwiązanie prowadzi do komunikatu „Zakończono” w moim środowisku:
źródło