Haben Sie ein Skript wie unten:
#!/bin/bash
#
# run this script. don't run it if it's already running.
#
PIDFILE=/tmp/script.pid
LOGFILE=script.log
if [[ -f $PIDFILE ]]; then
echo "$PIDFILE exists. Not going to run..."
exit 0
fi
sleep 10m >> $LOGFILE 2>&1 &
PID=$!
echo $PID > $PIDFILE
trap "echo Exiting...; rm $PIDFILE; exit $?" INT TERM EXIT KILL
wait $PID
Ich rufe dieses Skript wie folgt auf:
timeout 2m ./test_script
Bei einer Zeitüberschreitung oder Strg+C gibt das Skript zweimal „Beenden“ aus.
# timeout 2m ./test_script
^CExiting...
Exiting...
rm: cannot remove `/tmp/script.pid': No such file or directory
Unten sehen Sie die Ausgabe von strace und weitere Daten:
# ps -ef | grep -v grep | egrep -i "sleep|time"
root 8571 4690 0 12:17 pts/0 00:00:00 timeout 2m ./test_script
root 8572 8571 0 12:17 pts/0 00:00:00 /bin/bash ./test_script
root 8573 8572 0 12:17 pts/0 00:00:00 sleep 10m
# cat /tmp/script.pid
8573
# strace -p 8571
Process 8571 attached - interrupt to quit
wait4(-1, 0x7fffc43fad4c, 0, NULL) = ? ERESTARTSYS (To be restarted)
--- SIGINT (Interrupt) @ 0 (0) ---
kill(0, SIGINT) = 0
kill(0, SIGCONT) = 0
--- SIGCONT (Continued) @ 0 (0) ---
rt_sigreturn(0) = 61
--- SIGINT (Interrupt) @ 0 (0) ---
rt_sigreturn(0x2) = 61
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 8572
--- SIGCHLD (Child exited) @ 0 (0) ---
close(1) = 0
close(2) = 0
exit_group(0) = ?
Process 8571 detached
Kann mir jemand freundlicherweise dabei helfen, die internen Vorgänge zu verstehen, warum das Skript das Signal zweimal abfängt, um zweimal „Beenden …“ auszugeben?
Antwort1
Ersetzen Sie Ihre trap
Anweisung durch diese drei Zeilen:
trap "echo Exiting... INT; exit $?" INT
trap "echo Exiting... TERM; exit $?" TERM
trap "echo Exiting... EXIT; exit $?" EXIT
Sie erhalten die Ausgabe
Exiting... TERM
Exiting... EXIT
woraus wir folgern können
- Die
trap … TERM
Anweisung bewirkt, dass die Shell das SIGTERM-Signal abfängt. Dertimeout
Befehl sendet dem Prozess ein SIGTERM (standardmäßig), wenn das Timeout abläuft. Die Shell fängt also das Signal ab und führt den angegebenen Befehl aus, einschließlich derecho
, derrm
(in Ihrem eigentlichen Skript) und derexit
. - Die
trap … EXIT
Anweisung bewirkt, dass die Shell sich selbst eine Haftnotiz mit dem Hinweis „Denken Sie daran, dies zu tun, bevor ich nach Hause gehe“ hinterlässt. Wenn also die SIGTERM-Trap denexit
Befehl ausführt, wird die EXIT-Trap ausgeführt. - Wenn die EXIT-Trap den
exit
Befehl ausführt, wird das Skript tatsächlich beendet, anstatt die EXIT-Trap auszuführen und in das Rekursionsinferno zu geraten.
Wenn Sie Ctrl+ eingeben C, während das Skript ausgeführt wird, erhalten Sie die INT-Trap, gefolgt von der EXIT-Trap. Wenn Sie das Skript ohne timeout
oder mit einer Timeout-Dauer ausführen, die länger als die Ruhezeit ist, erhalten Sie nur die EXIT-Trap.
Es ist wahrscheinlich gut genug zu sagen
trap "exit $?" INT TERM
trap "echo Exiting...; rm $PIDFILE" EXIT
Ich glaube, dass die EXIT-Trap nicht ausgeführt werden muss exit
, weil Sie in die EXIT-Trap geraten, indem Sie einen exit
Befehl ausführen (einschließlich des impliziten Befehls am Ende des Skripts). Wenn Sie also mit der Ausführung der EXIT-Trap ( echo
und rm
) fertig sind, muss die Shell nichts weiter tun, als sich zu beenden. Die einzige Frage ist, mit welchem Beendigungsstatus das Skript beendet wird. Und wenn Sie einen Beendigungsstatuswert speichern und ausführen
rm $PIDFILE; exit $saved_status
, könnte das interessant sein. Aber solange Sie von sprechen rm $PIDFILE; exit $?
, wird das Skript wahrscheinlich mit dem Beendigungsstatus von beendet rm
; und das wird wahrscheinlich standardmäßig passieren, wenn dies rm
der letzte von Ihnen ausgeführte Befehl ist.
Ich habe ein paar schnelle Tests gemacht, die nahelegten, dass es möglich ist, die
trap "exit" INT TERM
Befehl, aber das verstehe ich nicht. Ihre Ergebnisse können abweichen.
PS: Wie Thomas sagte, trap … KILL
ist wirkungslos.