Was passiert ist, dass beide bash
und ping
das SIGINT empfangen ( bash
da beide nicht interaktiv sind ping
und bash
in 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 bash
Griffe dass SIGINT asynchron, erst nach dem aktuell laufenden Befehl verlassen. bash
Beendet 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
, sh
und sleep
erhalten SIGINT , wenn ich Strg-C drücken, aber sh
Ausgänge normalerweise mit einem 0 Exit - Code, so bash
ignoriert die SIGINT, weshalb wir „hier“ zu sehen.
ping
zumindest 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 ping
Ausführung die Tastenkombination Strg-C bash
drücken, werden Ctrl-C
die SIGINT-Handler zwar gedrückt , da sie jedoch ping
normal beendet werden, bash
jedoch nicht beendet.
Wenn Sie ein Add sleep 1
in dieser Schleife und drücken Sie Ctrl-C
während sleep
ausgeführt wird , weil sleep
keine spezielle Handler auf SIGINT hat, wird er sterben und Bericht an , bash
dass es eines SIGINT gestorben, und in diesem Fall bash
beendet (es wird sich mit SIGINT tatsächlich töten , um die Unterbrechung dem Elternteil zu melden).
bash
Ich 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 bash
Entwicklungs-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, ksh93
wenn ein Befehl von SIGINT beendet wird.
Sie erhalten das gleiche Verhalten wie bash
oben, 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 bash
das 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 bash
Skript handelt, wird dieses bash
Skript ebenfalls unterbrochen). Dies exit 130
ist 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
, ksh93
oder 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, wirdvi
nur 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 diefor
Schleife als Beispiel ein.)