我有两个脚本。 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
,则整件工作正常。那么我做错了什么?
答案 0 :(得分:1)
您的示例中有几个问题,最后我为您的问题找到了解决方案:
您的第一个脚本似乎错过了wait
语句,因此退出了
大约3秒后。但是script2
将保留在内存中
运行
你希望bash如何自动确定它应该在哪个进程中 发送SIGINT信号?
实际上bash
会在后台流程中停用SIGINT
(和SIGQUIT
)并且无法启用它们(您可以通过运行trap
进行检查单独命令检查设置陷阱的当前状态)。见How to send a signal SIGINT from script to script ? BASH
因此,script2
未在SIGINT
设置陷阱,因为它是后台流程,SIGINT
和SIGQUIT
都是被忽略,不能再被困,也不能在后台进程中重置。
作为参考,以下是与您的问题相关的bash文档:
处理组ID对后台进程的影响(在doc的作业控制部分中):
[...]进程组ID等于当前终端的进程 进程组ID [..]接收键盘生成的信号,如 SIGINT。据说这些过程处于前景。 后台进程是进程组ID不同的进程 终端的;这样的过程不受键盘生成的影响 信号强>
SIGINT
和SIGQUIT
的默认处理程序(在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范围)...因此,它们可以捕获或重置SIGINT
和SIGQUIT
信号。
然后诀窍是在子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"