Muchos ejemplos para trap
usar trap ... INT TERM EXIT
en tareas de limpieza. Pero, ¿es realmente necesario enumerar las tres sigspecs?
El manual dice:
Si un SIGNAL_SPEC es EXIT (0) ARG se ejecuta al salir del shell.
lo cual creo que se aplica si el script terminó normalmente o si terminó porque recibió SIGINT
o SIGTERM
. Un experimento también confirma mi creencia:
$ cat ./trap-exit
#!/bin/bash
trap 'echo TRAP' EXIT
sleep 3
$ ./trap-exit & sleep 1; kill -INT %1
[1] 759
TRAP
[1]+ Interrupt ./trap-exit
$ ./trap-exit & sleep 1; kill -TERM %1
[1] 773
TRAP
[1]+ Terminated ./trap-exit
Entonces, ¿por qué tantos ejemplos enumeran todo INT TERM EXIT
? ¿O me perdí algo y hay algún caso en el EXIT
que faltaría una suela ?
INT TERM EXIT
el código de limpieza se ejecuta dos veces cuandoSIGTERM
oSIGINT
se recibe.Respuestas:
La especificación POSIX no dice mucho sobre las condiciones que resultan en la ejecución de la trampa EXIT, solo sobre cómo debe verse su entorno cuando se ejecuta.
En el caparazón de cenizas de Busybox, su prueba de salida de trampa no hace eco 'TRAP' antes de salir debido a SIGINT o SIGTERM. Sospecho que existen otros proyectiles que pueden no funcionar de esa manera.
fuente
dash
Tampoco se atrapa justoEXIT
cuando recibeSIGINT/SIGTERM
.zsh
también, por lo tanto, tal vezbash
sea el único shell dondeEXIT
también coinciden las señales.zsh
no se atrapaEXIT
cuando recibeINT
, pero lo hace cuando recibeTERM
. EDITAR: Acabo de notar cuántos años tenía esto ...Sí, hay una diferencia.
Este script saldrá cuando lo presione Enter, o lo envíe
SIGINT
oSIGTERM
:Este script saldrá cuando presione Enter:
* Probado en sh , Bash y Zsh . (ya no funciona en sh cuando agrega un comando para que se ejecute trap)
También está lo que dijo @Shawn: Ash y Dash no atrapan las señales
EXIT
.Entonces, para manejar las señales de manera robusta, es mejor evitar las trampas por
EXIT
completo y usar algo como esto:fuente
mktemp
llamadas.exit
necesario encleanup
?ERR
para manejar eso, pero no es portátil .trap - INT TERM; kill -2 $$
como última línea de limpieza, decirle al shell principal que salió prematuramente. Si un shell primario foobar.sh llama a su script (foo.sh), y luego llama a bar.sh, no desea que bar.sh se ejecute si se envía INT / TERM a su foo.sh.trap cleanup EXIT
manejará esta propagación automáticamente, por lo que es IMO la más robusta. También significa que no tendría que llamarcleanup
al final del guión.Refinando la última respuesta, porque tiene problemas:
Puntos anteriores:
Los controladores INT y TERM no se dan por vencidos cuando pruebo: manejan el error y el shell vuelve a salir (y esto no es demasiado sorprendente). Por lo tanto, me aseguro de que la limpieza salga después, y en el caso de las señales siempre usa un código de error (y en el otro caso de una salida normal, conserva el código de error).
Con bash, parece que salir en el controlador INT también llama al controlador EXIT, por lo tanto, destapo el controlador de salida y lo llamo yo mismo (que funcionará en cualquier shell independientemente del comportamiento).
Atrapo la salida porque los scripts de shell pueden salir antes de llegar al fondo: errores de sintaxis, set -e y un retorno distinto de cero, simplemente llamando a exit. No puede confiar en que un shellscript llegue al fondo.
SIGQUIT es Ctrl- \ si nunca lo has probado. Obtiene un coredump adicional. Así que creo que también vale la pena atraparlo, incluso si es un poco oscuro.
La experiencia pasada dice que si (como yo) siempre presiona Ctrl-C varias veces, a veces lo atrapará a la mitad de la parte de limpieza de su script de shell, por lo que esto funciona, pero no siempre tan perfectamente como le gustaría.
fuente
trap
la persona que llama obtendría 130 para SIGINT, 143 para SIGTERM, etc. Así que me gustaría capturar y transmitir el código de salida correcto como:sig_cleanup() { err=$?; trap '' EXIT; (exit $err); cleanup; }
.trap '' EXIT INT TERM
la función de limpieza? ¿Es esto para evitar que el usuario interrumpa accidentalmente la limpieza que mencionó en el último párrafo? ¿No esEXIT
redundante?