Was ist los
Wenn Sie Ctrl+ drücken C, wird das SIGINT
Signal an die gesamte Vordergrundprozessgruppe gesendet . Hier wird es sowohl an den find
Prozess als auch an den aufrufenden Shell-Prozess gesendet . find
reagiert durch sofortiges Verlassen und die Shell reagiert durch Aufrufen der Falle.
Wenn der Code in der Trap zurückkehrt (dh nicht aufruft exit
), wird die Ausführung mit dem Befehl nach dem Befehl fortgesetzt, der durch das Signal unterbrochen wurde. Hier kommt nach dem find
Befehl das Ende des Skripts, sodass das Skript trotzdem sofort beendet wird. Sie können den Unterschied zwischen der Eingabe von 0 und 1 erkennen, indem Sie einen weiteren Befehl hinzufügen:
find /
echo "find returned $?"
Ein Weg, um zu tun, was Sie wollen (aber wahrscheinlich nicht tun sollten)
Du kannst machen was du willst; In diesem Teil meiner Antwort geht es jedoch mehr darum, die Shell-Programmierung zu entdecken als ein tatsächliches Problem zu lösen.
- Aus gestalterischer Sicht ist ein neu startbares Signal nicht das, was Sie normalerweise von relativ einfachen Programmen erwarten würden, die Shell-Skripte normalerweise sind. Die Erwartung ist, dass Ctrl+ Cdas Skript beendet.
- Wie Sie weiter unten sehen werden, werden die Funktionen der Shell ohnehin ein wenig erweitert.
Wenn Sie das Töten vermeiden möchten find
, müssen Sie es im Hintergrund starten : find / &
. Warten Sie dann mit dem wait
eingebauten Gerät, bis es normal beendet wird. Ein Signal unterbricht die wait
integrierte Funktion, die Sie in einer Schleife ausführen können, bis Sie ein Signal empfangen, das Sie weitergeben möchten. Verwenden Sie dann kill
, um den Job zu beenden.
hell () {
echo "Do you want to quit? Press 1 for yes and 0 for no"
read n
if [ "$n" = 1 ]; then
# Kill the job if it's running, then exit
if [ -n "$job_pid" ]; then kill $job_pid; fi
exit 1
fi
}
job_pid=
trap "hell" SIGINT
# Start a demo job in the background
for i in 1 2 3 4 5; do date; sleep 1; done &
job_pid=$!
# Call wait in a loop; wait will return 0 if the job exits, and 128+$signum if interrupted by a signal.
while ! wait; do
echo "resuming wait"
done
job_pid=
echo last exit code: $?
Es gibt Einschränkungen für diesen Ansatz in der Shell:
- Es gibt eine Race-Bedingung: Wenn Sie kurz nach Beendigung des Jobs, aber vor der Leitung, Ctrl+ drücken , versucht der Signalhandler zu töten , aber der Prozess existiert nicht mehr (selbst als Zombie, weil er bereits geerntet wurde) und der Prozess Die ID wurde möglicherweise von einem anderen Prozess wiederverwendet. Dies kann in der Shell nicht einfach behoben werden (möglicherweise durch Festlegen eines Handlers für ?).C
job_pid=
$jobpid
wait
SIGCHLD
- Wenn Sie den Rückgabestatus des Jobs benötigen, müssen Sie das
wait $job_pid
Formular verwenden. Aber dann kann man nicht unterscheiden, wait
ob "der Job durch ein Signal unterbrochen wurde" von "der Job wurde durch ein Signal beendet" (oder von "der Job wurde von selbst mit einem Rückgabestatus ≥128 beendet"), aber das ist eine allgemeine Tatsache in der Shell Programmierung).
- Dies lässt sich nicht leicht auf mehrere Unterjobs ausweiten. Beachten Sie, dass das Verhalten von Traps und Signalen oft überraschend ist, wenn Sie in den meisten Shell-Implementierungen über die Grundlagen hinausgehen (nur ksh macht dies gut).
Verwenden Sie eine schickere Sprache wie Perl oder Python, um diese Einschränkungen zu überwinden.