将SIGINT发送到前台进程但不是后台

时间:2014-08-26 17:00:18

标签: bash signals sigint bash-trap

我有两个脚本。 script1 生成 script2 ,然后向其发送SIGINT信号。但是 script2 中的陷阱似乎不起作用?!

SCRIPT1:

#!/bin/bash
./script2 &
sleep 1
kill -SIGINT $!
sleep 2

SCRIPT2:

#!/bin/bash
echo "~~ENTRY"
trap 'echo you hit ctrl-c, waking up...' SIGINT
sleep infinity
echo "~~EXIT"

如果将./script2 &更改为./script2并按CTRL+C,则整件工作正常。那么我做错了什么?

2 个答案:

答案 0 :(得分:1)

您的示例中有几个问题,最后我为您的问题找到了解决方案:

  • 您的第一个脚本似乎错过了wait语句,因此退出了 大约3秒后。但是script2将保留在内存中 运行

    你希望bash如何自动确定它应该在哪个进程中 发送SIGINT信号?

  • 实际上bash会在后台流程中停用SIGINT(和SIGQUIT)并且无法启用它们(您可以通过运行trap进行检查单独命令检查设置陷阱的当前状态)。见How to send a signal SIGINT from script to script ? BASH

    因此,script2未在SIGINT 设置陷阱,因为它是后台流程SIGINTSIGQUIT都是被忽略,不能再被困,也不能在后台进程中重置。

作为参考,以下是与您的问题相关的bash文档:

处理组ID对后台进程的影响(在doc的作业控制部分中):

  

[...]进程组ID等于当前终端的进程   进程组ID [..]接收键盘生成的信号,如   SIGINT。据说这些过程处于前景。   后台进程是进程组ID不同的进程   终端的;这样的过程不受键盘生成的影响   信号

SIGINTSIGQUIT的默认处理程序(在doc的信号部分):

  

bash运行的非内置命令将信号处理程序设置为shell从其父级继承的值。当作业控制不起作用时,除了这些继承的处理程序之外,异步命令会忽略SIGINT和SIGQUIT

关于陷阱的修改(在trap内置文档中):

  

进入shell时忽略的信号无法被捕获或重置

解决方案1 ​​

将您的script1修改为:

#!/bin/bash
{ ./script2; } &
sleep 1
subshell_pid=$!
pid=$(ps -ax -o ppid,pid --no-headers | sed -r 's/^ +//g;s/ +/ /g' |
                           grep "^$subshell_pid " | cut -f 2 -d " ")

kill -SIGINT $pid
sleep 2
wait      ## Don't forget this.

这是如何工作的?实际上,{}的使用将创建一个子shell,它将受到SIGINT的解释限制的限制,因为此子shell是后台进程。但是,子shell自己的子进程是前台进程而非后台进程(对于我们的子shell范围)...因此,它们可以捕获或重置SIGINTSIGQUIT信号。

然后诀窍是在子shell中找到这个子进程的pid,在这里我使用ps来查找唯一具有子shell的pid作为父pid的进程。

解决方案2

实际上,只有作为作业管理的直接新进程才会忽略其SIGINT和SIGQUIT。一个简单的bash函数不会被激活。因此,如果script2代码位于script1中的函数中,那么您的新script1将不再需要其他内容:

#!/bin/bash
script2() {
    ## script2 code
    echo "~~ENTRY"
    trap 'echo you hit ctrl-c, waking up...' SIGINT
    sleep infinity
    echo "~~EXIT"
}
## script1 code
script2 &
sleep 1
kill -SIGINT $!
sleep 2

这也可以。在场景背后,与解决方案1相同的机制正在发挥作用:bash函数非常接近{ }构造。

答案 1 :(得分:0)

我想你想要实现的是当script2收到SIGINT时它继续并打印消息。然后,你需要

#!/bin/bash
echo "~~ENTRY"
trap 'echo you hit ctrl-c, waking up...; CONT=true' SIGINT
CONT=false
while ! $CONT
do
   sleep 1
done
echo "~~EXIT"