有如下脚本:
#!/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
有人能帮助我理解为什么脚本两次捕获信号以打印“退出...”两次的内部原因吗?
答案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
语句使 shell 捕获 SIGTERM 信号。该timeout
命令在超时到期时向进程发送 SIGTERM(默认情况下)。因此,shell 捕获信号并执行指定的命令,包括echo
、rm
(在您的实际脚本中) 和exit
。 - 该
trap … EXIT
语句使 shell 给自己留下一张便条,上面写着“回家前记得做这件事”。因此,当 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
)时,shell 除了退出之外无需再做任何事情。唯一的问题是脚本以什么退出状态退出。并且,如果您保存了某些退出状态值并执行
rm $PIDFILE; exit $saved_status
,那可能会很有趣。但只要您谈论的是rm $PIDFILE; exit $?
,脚本可能会以的退出状态退出;如果是您执行的最后一个命令,rm
则默认情况下可能会发生这种情况。rm
我做了一些快速测试,结果表明可以省略
trap "exit" INT TERM
命令,但我不明白。YMMV。
PS 正如托马斯所说,trap … KILL
是无效的。