有一個腳本如下:
#!/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 時,腳本會列印“Exiting”兩次。
# 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
有人可以幫助我理解為什麼腳本兩次捕獲信號以打印“Exiting...”兩次的內部原理嗎?
答案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+ ,您將收到 INT 陷阱,然後是 EXIT 陷阱。如果您在沒有或逾時持續時間長於睡眠時間的情況下C執行腳本,您將僅收到 EXIT 陷阱。timeout
可能足以說
trap "exit $?" INT TERM
trap "echo Exiting...; rm $PIDFILE" EXIT
我認為 EXIT 陷阱不需要執行,因為您透過執行命令(包括腳本末尾的隱式命令)exit
進入 EXIT 陷阱,因此,當您執行完 EXIT 陷阱(和)時, shell 除了退出之外沒有什麼可做的。唯一的問題是腳本以什麼退出狀態退出。而且,如果您正在保存一些退出狀態值並執行 ,這可能會很有趣。但只要您正在談論,腳本就可能會以;的退出狀態退出。如果這是您執行的最後一個命令,則預設情況下可能會發生這種情況。exit
echo
rm
rm $PIDFILE; exit $saved_status
rm $PIDFILE; exit $?
rm
rm
我做了一些快速測試,結果顯示可以省略
trap "exit" INT TERM
命令,但我不明白。 YMMV。
PS 正如湯瑪斯所說,trap … KILL
是無效的。