Есть сценарий, как показано ниже:
#!/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 раза?
linux
bash
bash-scripting
cog_n1t1v3
источник
источник
rm: cannot remove /tmp/script.pid: No such file or directory
<- вы выполняетеecho "Exiting..."
дважды, вы уверены, что вам нужно всеINT TERM EXIT KILL
- может ли случиться так, что два из них сработают - одновременно - когда вы нажмете CTRL-C?trap
. Что касается сценария, требующего перехвата всех 4 указанных сигналов, да, они мне нужны все. В качестве альтернативы я просто использую тайм-аут из моего скрипта. Это решает проблему двойного вызова, когдаtimeout
завершаетсяKILL
, так что никто не считается. Однако, если я запускаю ваш скрипт с использованием bash 4.1.5, я вижу только одно сообщение дляTERM
(когда он завершается) и никакого выхода дляINT
. Возможно, версия bash имеет значение.Ответы:
Если вы замените свое
trap
утверждение на эти три строки:вы получите выход
из которого мы можем вывести
trap … TERM
Утверждение заставляет оболочку для улавливания сигнала SIGTERM. Командаtimeout
отправляет процессу SIGTERM (по умолчанию) по истечении времени ожидания. Таким образом, оболочка ловит сигнал и выполняет указанную команду, в том числеecho
,rm
(в вашем текущем скрипте) иexit
.trap … EXIT
заявление заставляет оболочку оставлять себе заметку со словами «не забудьте сделать это, прежде чем я уйду домой». Таким образом, когда ловушка SIGTERM выполняетexit
команду, ловушка EXIT выполняется.exit
команду, скрипт фактически завершается, а не выполняет ловушку EXIT и отправляется в ад рекурсии.Если вы нажмете Ctrl+ Cво время выполнения скрипта, вы получите ловушку INT, а затем ловушку EXIT. Если вы запустите сценарий без
timeout
или с временем ожидания, превышающим время ожидания, вы получите только ловушку EXIT.Это, наверное, достаточно хорошо, чтобы сказать
Я считаю, что ловушка EXIT не должна выполняться
exit
, потому что вы попадаете в ловушку EXIT, выполняяexit
команду (включая неявную в конце сценария), поэтому, когда вы закончите выполнение ловушки EXIT (echo
иrm
), оболочке больше нечего делать, кроме выхода. Вопрос только в том, с каким статусом выхода выходит сценарий. И, если вы сохраняете какое-то значение состояния выхода и делаетеrm $PIDFILE; exit $saved_status
, это может быть интересно. Но пока вы говоритеrm $PIDFILE; exit $?
, скрипт, вероятно, завершится со статусом выхода изrm
; и это, вероятно, произойдет по умолчанию, еслиrm
последняя команда, которую вы выполняете.Я сделал несколько быстрых тестов, которые показали, что можно оставить
команда, но я не понимаю этого. YMMV.
PS Как сказал Томас,
trap … KILL
неэффективно.источник