我从中学到了https://unix.stackexchange.com/a/230568/674收到 SIGINT 后将以ping
0 退出,这允许包含命令的 bash 脚本ping
继续运行而不是退出。
我有一个具有类似行为的脚本:
#!/bin/bash
while true; do
sudo -S sleep 4;
echo $?
sudo -k;
done
当我运行它时,当它要求我输入密码时,我输入 Ctrl-C,并且脚本不会退出,而是继续运行。唯一的区别是,sudo
收到 SIGINT 后,退出时为 1 而不是 0。所以我想知道为什么 bash 脚本不退出而是继续运行?谢谢。
$ ./test.sh
[sudo] password for t:
1
[sudo] password for t:
1
[sudo] password for t:
1
...
答案1
该测试不是简单的“命令是否成功”测试。当进程以 SIGINT 退出时,可以通过以下方式读取其退出状态wait(2)
。通过使用WIFSIGNALED
和WTERMSIG
,bash 可以判断子进程(在本例中为 sudo)是否被信号直接杀死。
以下是按 Ctrl+C 时系统调用的结果cat
(根据strace
):
wait4(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}], 0, NULL) = 9357
这是在 中按 Ctrl+C 时的结果sudo -S sleep 4
:
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 1}], 0, NULL) = 9479
为了完整起见,以下是 Ctrl+C'ing 的结果ping localhost
:
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 9710
答案2
显然,该sudo
命令处理 INT 信号,以便它只是退出,与例如不同sleep
。因此,Bash 认为它不必在 SIGNIT 的默认处理程序中杀死自己:
bash 是少数几个在处理 SIGINT/SIGQUIT 传递时实现等待和协作退出方法的 shell 之一。解释脚本时,收到 SIGINT 后,它不会立即退出,而是等待当前正在运行的命令返回,并且只有在该命令也被该 SIGINT 杀死时才退出(通过使用 SIGINT 杀死自身)。这个想法是,例如,如果您的脚本调用 vi,并且您在 vi 中按 Ctrl+C 取消操作,则不应将其视为中止脚本的请求。
您可以通过定义 SIGINT 陷阱来覆盖此行为:
trap 'trap - INT; kill -s INT $$' INT
参考:https://mywiki.wooledge.org/SignalTrap(第 4 章和第 5 章)