Was passiert ist, dass beide bashund pingdas SIGINT empfangen ( bashda beide nicht interaktiv sind pingund bashin derselben Prozessgruppe ausgeführt werden, die von der interaktiven Shell, von der aus Sie das Skript ausgeführt haben, erstellt und als Vordergrundprozessgruppe des Terminals festgelegt wurde).
Allerdings bashGriffe dass SIGINT asynchron, erst nach dem aktuell laufenden Befehl verlassen. bashBeendet sich erst nach Erhalt dieses SIGINT, wenn der aktuell ausgeführte Befehl von einem SIGINT abbricht (dh sein Beendigungsstatus zeigt an, dass er von SIGINT getötet wurde).
$ bash -c 'sh -c "trap exit\ 0 INT; sleep 10; :"; echo here'
^Chere
Oben bash, shund sleeperhalten SIGINT , wenn ich Strg-C drücken, aber shAusgänge normalerweise mit einem 0 Exit - Code, so bashignoriert die SIGINT, weshalb wir „hier“ zu sehen.
pingzumindest der von iputils verhält sich so. Bei einer Unterbrechung werden Statistiken gedruckt und mit einem Ausgangsstatus von 0 oder 1 beendet, je nachdem, ob die Pings beantwortet wurden oder nicht. Wenn Sie also während der pingAusführung die Tastenkombination Strg-C bashdrücken, werden Ctrl-Cdie SIGINT-Handler zwar gedrückt , da sie jedoch pingnormal beendet werden, bashjedoch nicht beendet.
Wenn Sie ein Add sleep 1in dieser Schleife und drücken Sie Ctrl-Cwährend sleepausgeführt wird , weil sleepkeine spezielle Handler auf SIGINT hat, wird er sterben und Bericht an , bashdass es eines SIGINT gestorben, und in diesem Fall bashbeendet (es wird sich mit SIGINT tatsächlich töten , um die Unterbrechung dem Elternteil zu melden).
bashIch bin mir nicht sicher , warum sich das so verhält, und ich stelle fest, dass das Verhalten nicht immer deterministisch ist. Ich habe gerade die Frage auf der bashEntwicklungs-Mailingliste gestellt ( Update : @Jilles hat jetzt den Grund in seiner Antwort festgehalten ).
Die einzige andere Shell, die sich ähnlich verhält, ist ksh93 (Update, wie von @Jilles erwähnt, auch FreeBSDsh ). Dort scheint SIGINT einfach ignoriert zu werden. Und wird beendet, ksh93wenn ein Befehl von SIGINT beendet wird.
Sie erhalten das gleiche Verhalten wie bashoben, aber auch:
ksh -c 'sh -c "kill -INT \$\$"; echo test'
Gibt "test" nicht aus. Das heißt, es beendet sich (indem es sich dort mit SIGINT umbringt), wenn der Befehl, auf den es gewartet hat, von SIGINT stirbt, auch wenn es selbst diesen SIGINT nicht erhalten hat.
Ein Workaround wäre, folgendes hinzuzufügen:
trap 'exit 130' INT
Oben im Skript, um bashdas Beenden beim Empfang eines SIGINT zu erzwingen (beachten Sie, dass SIGINT in jedem Fall nicht synchron verarbeitet wird, nur nachdem der aktuell ausgeführte Befehl beendet wurde).
Idealerweise möchten wir unseren Eltern mitteilen, dass wir an einem SIGINT gestorben sind (wenn es sich also beispielsweise um ein anderes bashSkript handelt, wird dieses bashSkript ebenfalls unterbrochen). Dies exit 130ist nicht dasselbe wie das Sterben von SIGINT (obwohl einige Shells in $?beiden Fällen den gleichen Wert annehmen), es wird jedoch häufig verwendet, um einen Tod von SIGINT zu melden (auf Systemen, bei denen SIGINT 2 ist, was am meisten ist).
Doch für bash, ksh93oder FreeBSD sh, das nicht funktioniert. Dieser 130-Exit-Status wird von SIGINT nicht als Tod betrachtet, und ein übergeordnetes Skript würde dort nicht abgebrochen.
Eine möglicherweise bessere Alternative wäre es, sich mit SIGINT zu töten, wenn Sie SIGINT erhalten:
trap '
trap - INT # restore default INT handler
kill -s INT "$$"
' INT
for f in *.txt; do vi "$f"; cp "$f" newdir; done. Wenn der Benutzer beim Bearbeiten einer der Dateien Strg + C eingibt, wirdvinur eine Meldung angezeigt. Es erscheint vernünftig, dass die Schleife fortgesetzt wird, nachdem der Benutzer die Bearbeitung der Datei abgeschlossen hat. (Und ja, ich weiß, dass Sie sagen könntenvi *.txt; cp *.txt newdir; ich reiche nur dieforSchleife als Beispiel ein.)