如果当前运行的子进程处理了 SIGINT,为什么 Bash 会忽略它?

如果当前运行的子进程处理了 SIGINT,为什么 Bash 会忽略它?

如果我用以下命令中断以下 Bash 循环,它就会停止^C

(while true; do sleep 5; done)

然而,以下情况则不然:

(while true; do aplay test.wav; done)

据我所知,区别在于aplaycatch 和 handler SIGINT,而sleep没有。

为什么是这样? POSIX 是否在某处指定了这种行为(我注意到 Dash 做了同样的事情),如果是,为什么?我不太明白为什么这种行为是可取的。事实上,Bash 怎样才能区分出来呢?我必须承认我不知道任何机制(至少除了ptrace黑客/proc/之外)一个进程可以告诉另一个进程如何处理信号。

另外,有没有办法抵消呢?我注意到即使是trap 'exit 0' SIGINTbefore 循环也没有帮助。

编辑:在子 shell 中运行循环很重要,因为否则父 shell 将不会收到SIGINT。)

答案1

问题已经很好解释了这里, 作为世界教育理事会 等待并配合退出我鼓励你阅读它。基本上,您的信号会被所有前台进程接收,即 shell 和程序、sleep 或 aplay。如果程序不处理该信号,则退出并返回代码 130。 shell 等待子进程结束,并看到这一点,并且事实上它也收到了信号,因此退出。

当程序捕获信号时,它通常只是以代码 1 退出(与 aplay 一样)。当 shell 等待子进程时,它发现它并未因信号而结束,因此必须假设该信号是程序工作的正常方面,因此 shell 会正常运行。

对于您的示例,处理 aplay 的最佳方法是检查其返回代码是否非零并停止:

(while aplay test.wav; do :; done)

上述文章继续解释说,一个行为良好的程序想要捕获 sigint 来进行一些清理,那么应该禁用其处理程序,并重新终止自身以获得正确的退出标志设置。

答案2

在子进程完成之前,脚本不会收到信号。但是如果你在后台运行它然后等待它,父进程将要获取信号:

(while true; do aplay test.wav; done) &
wait $!

答案3

你可以试试这个,我遇到过这个问题几次,我不知道为什么它会起作用。

$(sigint)

这是为了像 bash 中的 var 一样显示结果,但要做到这一点,它必须运行它,并且有时可以工作。

相关内容