Quello che succede è che sia bashe pingricevere il SIGINT ( bashessendo non interattivi, sia pinge bashgestiscono nello stesso gruppo processo che è stato creato e insieme come gruppo processo in primo piano del terminale dalla shell interattiva è stato eseguito lo script da).
Tuttavia, bashgestisce SIGINT in modo asincrono, solo dopo la chiusura del comando attualmente in esecuzione. bashesce solo dopo aver ricevuto quel SIGINT se il comando attualmente in esecuzione muore di un SIGINT (cioè il suo stato di uscita indica che è stato ucciso da SIGINT).
$ bash -c 'sh -c "trap exit\ 0 INT; sleep 10; :"; echo here'
^Chere
Sopra, bash, she sleepricevere SIGINT quando si preme Ctrl-C, ma shle uscite di solito con un codice di uscita 0, quindi bashignora il SIGINT, ed è per questo che vediamo "qui".
ping, almeno quello di iputils, si comporta così. Se interrotto, stampa le statistiche ed esce con uno stato di uscita 0 o 1 a seconda che i ping abbiano o meno risposto. Quindi, quando premi Ctrl-C mentre pingè in esecuzione, bashnota che hai premuto Ctrl-Cnei suoi gestori SIGINT, ma poiché pingesce normalmente, bashnon esce.
Se aggiungi un sleep 1in quel ciclo e premi Ctrl-Cmentre sleepè in esecuzione, perché sleepnon ha un gestore speciale su SIGINT, morirà e segnalerà bashche è morto di un SIGINT, e in quel caso bashuscirà (in realtà si ucciderà con SIGINT in modo che per segnalare l'interruzione al suo genitore).
Quanto al perché bashsi comporti così, non ne sono sicuro e noto che il comportamento non è sempre deterministico. Ho appena fatto la domanda sulla bashmailing list di sviluppo ( Aggiornamento : @Jilles ha ora individuato il motivo nella sua risposta ).
L'unica altra shell che ho trovato che si comporta in modo simile è ksh93 (Update, come menzionato da @Jilles, così fa FreeBSDsh ). Lì, SIGINT sembra essere chiaramente ignorato. Ed ksh93esce ogni volta che un comando viene ucciso da SIGINT.
Ottieni lo stesso comportamento di cui bashsopra ma anche:
ksh -c 'sh -c "kill -INT \$\$"; echo test'
Non genera "test". Cioè, esce (uccidendosi con SIGINT lì) se il comando che stava aspettando muore di SIGINT, anche se esso stesso non ha ricevuto quel SIGINT.
Una soluzione sarebbe aggiungere un:
trap 'exit 130' INT
Nella parte superiore dello script per forzare bashl'uscita al momento della ricezione di un SIGINT (notare che in ogni caso, SIGINT non verrà elaborato in modo sincrono, solo dopo la chiusura del comando attualmente in esecuzione).
Idealmente, vorremmo segnalare al nostro genitore che siamo morti per un SIGINT (quindi se si tratta di un altro bashscript, ad esempio, anche quello bashscript viene interrotto). Fare un exit 130non è lo stesso che morire di SIGINT (anche se alcune shell imposteranno $?lo stesso valore per entrambi i casi), tuttavia è spesso usato per segnalare un decesso di SIGINT (sui sistemi in cui SIGINT è 2 che è il più).
Tuttavia, per bash, ksh93o FreeBSD sh, che non funziona. Quel 130 stato di uscita non è considerato un decesso da SIGINT e uno script genitore non si interromperà lì.
Quindi, un'alternativa forse migliore sarebbe ucciderci con SIGINT dopo aver ricevuto SIGINT:
trap '
trap - INT # restore default INT handler
kill -s INT "$$"
' INT
for f in *.txt; do vi "$f"; cp "$f" newdir; done. Se l'utente digita Ctrl + C durante la modifica di uno dei file,vivisualizza solo un messaggio. Sembra ragionevole che il ciclo continui dopo che l'utente ha finito di modificare il file. (E sì, lo so che potresti dirlovi *.txt; cp *.txt newdir; sto solo presentando ilforloop come esempio.)