Co się dzieje, że zarówno bash
i ping
otrzyma SIGINT ( bash
istota nie interaktywne, zarówno ping
i bash
działać w tej samej grupie procesów, które zostały utworzone i ustawione jako grupy procesów planie terminala przez interaktywnej powłoki uruchomiono ten skrypt z).
Jednak bash
obsługuje SIGINT asynchronicznie, dopiero po zakończeniu aktualnie uruchomionej komendy. bash
kończy działanie dopiero po otrzymaniu tego SIGINT, jeśli aktualnie uruchomione polecenie umiera z SIGINT (tzn. jego status wyjścia wskazuje, że został zabity przez SIGINT).
$ bash -c 'sh -c "trap exit\ 0 INT; sleep 10; :"; echo here'
^Chere
Powyżej bash
, sh
i sleep
otrzymać SIGINT po naciśnięciu Ctrl-C, ale sh
wychodzi zwykle z kodem 0 wyjścia, więc bash
pomija SIGINT, dlatego widzimy „tutaj”.
ping
, przynajmniej ten z iputils, zachowuje się tak. Po przerwaniu drukuje statystyki i wychodzi ze statusem wyjścia 0 lub 1 w zależności od tego, czy odpowiedzi na ping zostały wysłane. Tak więc, gdy naciśniesz Ctrl-C podczas ping
działania, bash
notatki, które nacisnąłeś Ctrl-C
w jego procedurach obsługi SIGINT, ale ponieważ ping
wychodzą normalnie, bash
nie wychodzą.
Jeśli dodasz sleep 1
w tej pętli i naciśniesz Ctrl-C
podczas sleep
działania, ponieważ sleep
nie ma specjalnego modułu obsługi w SIGINT, umrze i zgłosi bash
, że zmarł w wyniku SIGINT, i w takim przypadku bash
zakończy działanie (faktycznie zabije się za pomocą SIGINT, tak że zgłosić przerwanie rodzicowi).
Co do tego, dlaczego bash
tak się zachowuje, nie jestem pewien i zauważam, że zachowanie nie zawsze jest deterministyczne. Właśnie zadałem pytanie na bash
liście mailingowej dla programistów ( aktualizacja : @Jilles podał teraz powód swojej odpowiedzi ).
Jedyną znalezioną przeze mnie powłoką, która zachowuje się podobnie, jest ksh93 (aktualizacja, o której wspomina @Jilles, podobnie jak FreeBSDsh
). Tam SIGINT wydaje się wyraźnie ignorowany. I ksh93
kończy działanie, gdy polecenie jest zabijane przez SIGINT.
Otrzymujesz takie samo zachowanie jak bash
powyżej, ale także:
ksh -c 'sh -c "kill -INT \$\$"; echo test'
Nie wyświetla „testu”. Oznacza to, że kończy działanie (zabijając się tam za pomocą SIGINT), jeśli polecenie, na które czekał, umiera SIGINT, nawet jeśli samo nie otrzymało tego SIGINT.
Obejściem byłoby dodanie:
trap 'exit 130' INT
W górnej części skryptu, aby wymusić bash
wyjście po otrzymaniu SIGINT (pamiętaj, że w każdym razie SIGINT nie będzie przetwarzany synchronicznie, tylko po zakończeniu aktualnie uruchomionej komendy).
Najlepiej byłoby, gdybyśmy chcieli zgłosić naszemu rodzicowi, że zmarliśmy z powodu SIGINT (aby bash
na przykład w przypadku innego skryptu ten bash
skrypt również został przerwany). Wykonanie an exit 130
to nie to samo, co umieranie SIGINT (chociaż niektóre powłoki ustawią się $?
na tę samą wartość w obu przypadkach), jednak często jest używane do zgłaszania śmierci przez SIGINT (w systemach, w których SIGINT to 2, co jest najbardziej).
Jednak dla bash
, ksh93
lub FreeBSD sh
, że nie działa. Ten status wyjścia 130 nie jest uważany przez SIGINT za śmierć, a skrypt nadrzędny nie przerywałby tam.
Zatem prawdopodobnie lepszą alternatywą byłoby zabicie się SIGINT po otrzymaniu SIGINT:
trap '
trap - INT # restore default INT handler
kill -s INT "$$"
' INT
for f in *.txt; do vi "$f"; cp "$f" newdir; done
. Jeśli użytkownik wpisze Ctrl + C podczas edycji jednego z plików,vi
po prostu wyświetli komunikat. Wydaje się uzasadnione, że pętla powinna być kontynuowana po zakończeniu edycji pliku przez użytkownika. (I tak, wiem, że można powiedziećvi *.txt; cp *.txt newdir
; po prostu przesyłamfor
pętlę jako przykład.)