Tenga un guión como el siguiente:
#!/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
Estoy invocando este script de la siguiente manera:
timeout 2m ./test_script
Cuando se agota el tiempo de espera o Ctrl+c, el script imprime "Saliendo" dos veces.
# timeout 2m ./test_script
^CExiting...
Exiting...
rm: cannot remove `/tmp/script.pid': No such file or directory
A continuación se muestra el resultado de strace y más datos:
# 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
¿Puede alguien ayudarme a comprender los aspectos internos de por qué el script atrapa la señal dos veces para imprimir "Saliendo..." 2 veces?
Respuesta1
Si reemplaza su trap
declaración con estas tres líneas:
trap "echo Exiting... INT; exit $?" INT
trap "echo Exiting... TERM; exit $?" TERM
trap "echo Exiting... EXIT; exit $?" EXIT
obtendrás el resultado
Exiting... TERM
Exiting... EXIT
de donde podemos deducir
- La
trap … TERM
declaración hace que el shell capture la señal SIGTERM. Eltimeout
comando envía al proceso un SIGTERM (de forma predeterminada) cuando expira el tiempo de espera. Entonces, el shell capta la señal y ejecuta el comando especificado, incluido elecho
, elrm
(en su script real) y elexit
. - La
trap … EXIT
declaración hace que el caparazón deje una nota adhesiva que dice "recuerda hacer esto antes de irme a casa". Entonces, cuando la trampa SIGTERM ejecuta elexit
comando, se ejecuta la trampa EXIT. - Cuando la trampa EXIT ejecuta el
exit
comando, el script realmente sale, en lugar de ejecutar la trampa EXIT y adentrarse en el infierno de la recursividad.
Si escribe Ctrl+ Cmientras se ejecuta el script, obtendrá la captura INT seguida de la captura EXIT. Si ejecuta el script sin timeout
o con un tiempo de espera mayor que el tiempo de suspensión, solo obtendrá la captura EXIT.
Probablemente sea suficiente decir
trap "exit $?" INT TERM
trap "echo Exiting...; rm $PIDFILE" EXIT
Creo que no es necesario ejecutar la trampa EXIT exit
, porque ingresas a la trampa EXIT ejecutando un exit
comando (incluido el implícito al final del script), por lo que, cuando termines de ejecutar la trampa EXIT ( echo
y rm
), al shell no le queda nada más que hacer que salir. La única pregunta es con qué estado de salida sale el script. Y, si estuviera guardando algún valor de estado de salida y haciendo
rm $PIDFILE; exit $saved_status
, eso podría ser interesante. Pero mientras esté hablando de rm $PIDFILE; exit $?
, el script probablemente saldrá con el estado de salida de rm
; y eso probablemente sucederá de forma predeterminada si rm
es el último comando que ejecuta.
Hice algunas pruebas rápidas que sugirieron que es posible dejar de lado el
trap "exit" INT TERM
comando, pero no lo entiendo. YMMV.
PD: Como dijo Thomas, trap … KILL
es ineficaz.