Имейте сценарий, как показано ниже:
#!/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
Я вызываю этот скрипт следующим образом:
timeout 2m ./test_script
По истечении времени ожидания или при нажатии ctrl+c скрипт дважды выводит сообщение «Выход».
# timeout 2m ./test_script
^CExiting...
Exiting...
rm: cannot remove `/tmp/script.pid': No such file or directory
Ниже приведен вывод strace и дополнительные данные:
# 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
Может ли кто-нибудь любезно помочь мне разобраться во внутренних механизмах скрипта, почему он дважды перехватывает сигнал, чтобы вывести сообщение «Выход...» 2 раза?
решение1
Если вы замените свое trap
утверждение этими тремя строками:
trap "echo Exiting... INT; exit $?" INT
trap "echo Exiting... TERM; exit $?" TERM
trap "echo Exiting... EXIT; exit $?" EXIT
вы получите результат
Exiting... TERM
Exiting... EXIT
из чего мы можем сделать вывод
- Оператор
trap … TERM
заставляет оболочку перехватывать сигнал SIGTERM.timeout
Команда отправляет процессу сигнал SIGTERM (по умолчанию) по истечении тайм-аута. Таким образом, оболочка перехватывает сигнал и выполняет указанную команду, включаяecho
,rm
(в вашем фактическом скрипте) иexit
. - Оператор
trap … EXIT
заставляет оболочку оставить себе записку с надписью «не забудь сделать это, прежде чем я пойду домой». Таким образом, когда ловушка SIGTERM выполняет командуexit
, выполняется ловушка EXIT. - Когда ловушка EXIT выполняет
exit
команду, скрипт фактически завершает работу, а не выполняет ловушку EXIT и не уходит в ад рекурсии.
Если вы введете Ctrl+ Cво время работы скрипта, вы получите ловушку INT, за которой последует ловушка EXIT. Если вы запустите скрипт без timeout
, или с длительностью тайм-аута, которая больше времени сна, вы получите только ловушку EXIT.
Вероятно, достаточно сказать:
trap "exit $?" INT TERM
trap "echo Exiting...; rm $PIDFILE" EXIT
Я считаю, что ловушка EXIT не нуждается в выполнении exit
, потому что вы попадаете в ловушку EXIT, выполняя exit
команду (включая неявную в конце скрипта), поэтому, когда вы заканчиваете выполнение ловушки EXIT ( echo
и rm
), оболочке ничего не остается делать, кроме как выйти. Единственный вопрос в том, с каким статусом выхода скрипт завершается. И, если вы сохраняли некоторое значение статуса выхода и делали
rm $PIDFILE; exit $saved_status
, это могло бы быть интересно. Но пока вы говорите о rm $PIDFILE; exit $?
, скрипт, вероятно, завершится со статусом выхода rm
; и это, вероятно, произойдет по умолчанию, если rm
это последняя команда, которую вы выполняете.
Я провел несколько быстрых тестов, которые показали, что можно отказаться от
trap "exit" INT TERM
команда, но я этого не понимаю. YMMV.
P.S. Как сказал Томас, trap … KILL
это неэффективно.