您的示例中有几个问题,最后我为您的问题提供了解决方案:
您的第一个脚本似乎错过了一条wait
语句,因此,它在大约 3 秒后退出。但是script2
将保留在内存中并运行。
您希望 bash 如何自动确定应该发送 SIGINT 信号的进程?
实际上bash
将在后台进程上禁用SIGINT
(和SIGQUIT
)并且它们无法启用(您可以通过trap
单独运行命令来检查设置陷阱的当前状态)。请参阅如何从脚本发送信号 SIGINT 到脚本?重击
所以你script2
没有设置陷阱,SIGINT
因为它是一个后台进程,两者SIGINT
都SIGQUIT
被忽略并且不能再被困或在后台进程中重置。
作为参考,以下是 bash 中与您的问题相关的文档:
进程组 id 对后台进程的影响(在文档的作业控制部分):
[...] 进程组 ID 等于当前终端进程组 ID [..] 的进程接收键盘生成的信号,例如 SIGINT。据说这些过程处于前台。
后台进程是那些进程组 ID 与终端不同的进程;这样的过程不受键盘产生的信号的影响。
SIGINT
和的默认处理程序SIGQUIT
(在文档的信号部分中):
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.
这是如何运作的 ?实际上,使用{
and}
将创建一个子shell,这将受到解释限制的限制SIGINT
,因为这个子shell是一个后台进程。但是,子shell 自己的子进程是前台而不是后台进程(对于我们的子shell 范围)......因此,它们可以捕获或重置SIGINT
并SIGQUIT
发出信号。
然后诀窍是在子shell中找到这个子ps
进程的pid,在这里我用来找到唯一一个将子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 函数非常接近{
}
构造。