为什么这个脚本在收到SIGINT后继续运行?

为什么这个脚本在收到SIGINT后继续运行?

我从中学到了https://unix.stackexchange.com/a/230568/674收到 SIGINT 后将以ping0 退出,这允许包含命令的 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)。通过使用WIFSIGNALEDWTERMSIG,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 章)

相关内容