Tenha um script conforme abaixo:
#!/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
Estou invocando este script conforme abaixo:
timeout 2m ./test_script
No tempo limite ou ctrl+c, o script imprime "Exiting" duas vezes.
# timeout 2m ./test_script
^CExiting...
Exiting...
rm: cannot remove `/tmp/script.pid': No such file or directory
Abaixo está a saída do strace e mais dados:
# 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
Alguém pode me ajudar a entender os detalhes internos de por que o script captura o sinal duas vezes para imprimir "Saindo..." 2 vezes?
Responder1
Se você substituir sua trap
declaração por estas três linhas:
trap "echo Exiting... INT; exit $?" INT
trap "echo Exiting... TERM; exit $?" TERM
trap "echo Exiting... EXIT; exit $?" EXIT
você obterá a saída
Exiting... TERM
Exiting... EXIT
do qual podemos deduzir
- A
trap … TERM
instrução faz com que o shell capture o sinal SIGTERM. Otimeout
comando envia ao processo um SIGTERM (por padrão) quando o tempo limite expira. Portanto, o shell captura o sinal e executa o comando especificado, incluindo oecho
, orm
(no seu script real) e oexit
. - A
trap … EXIT
declaração faz com que o shell deixe um bilhete dizendo "lembre-se de fazer isso antes de ir para casa". Portanto, quando a armadilha SIGTERM executa oexit
comando, a armadilha EXIT é executada. - Quando a armadilha EXIT executa o
exit
comando, o script realmente sai, em vez de executar a armadilha EXIT e entrar no inferno de recursão.
Se você digitar Ctrl+ Cenquanto o script estiver em execução, obterá a armadilha INT seguida pela armadilha EXIT. Se você executar o script sem timeout
ou com uma duração de tempo limite maior que o tempo de suspensão, você obterá apenas a armadilha EXIT.
Provavelmente é bom o suficiente para dizer
trap "exit $?" INT TERM
trap "echo Exiting...; rm $PIDFILE" EXIT
Acredito que a armadilha EXIT não precisa executar exit
, pois você entra na armadilha EXIT executando um exit
comando (inclusive o implícito no final do script), então, quando terminar de executar a armadilha EXIT ( echo
e rm
), o shell não tem mais nada a fazer a não ser sair. A única questão é com qual status de saída o script sai. E, se você estivesse salvando algum valor de status de saída e fazendo isso
rm $PIDFILE; exit $saved_status
, isso poderia ser interessante. Mas enquanto você estiver falando rm $PIDFILE; exit $?
, o script provavelmente sairá com o status de saída do rm
; e isso provavelmente acontecerá por padrão se rm
for o último comando executado.
Fiz alguns testes rápidos que sugeriram que é possível deixar de lado o
trap "exit" INT TERM
comando, mas não entendo isso. YMMV.
PS Como disse Thomas, trap … KILL
é ineficaz.