有没有办法调用子进程,以便它及其所有后代发送一个中断,就像你Ctrl-C前台任务一样?我正在尝试杀死一个调用一个长期运行的孩子的启动器脚本。我已经尝试kill -SIGINT $child
(它不会向其后代发送中断,因此是无操作)和kill -SIGINT -$child
(在交互式调用时有效但在脚本中运行时无效)。
这是一个测试脚本。长时间运行的脚本是test.sh --child
。当您致电test.sh --parent
时,它会调用test.sh --child &
,然后尝试将其杀死。如何让父母成功杀死孩子?
#!/bin/bash
if [ "$1" = "--child" ]; then
sleep 1000
elif [ "$1" = "--parent" ]; then
"$0" --child &
for child in $(jobs -p); do
echo kill -SIGINT "-$child" && kill -SIGINT "-$child"
done
wait $(jobs -p)
else
echo "Must be invoked with --child or --parent."
fi
我知道您可以将长时间运行的子项修改为trap
信号,将它们发送到其子进程,然后等待(从
Bash script kill background (grand)children on Ctrl+C),但是没有修改子脚本的方法吗?
答案 0 :(得分:10)
对于任何想知道的人,这就是你如何在后台启动孩子并在ctrl + c上杀死他们:
#!/usr/bin/env bash
command1 &
pid[0]=$!
command2 &
pid[1]=$!
trap "kill ${pid[0]} ${pid[1]}; exit 1" INT
wait
答案 1 :(得分:5)
somecommand &
在$!
somecommand &
pid[0]=$!
anothercommand &
pid[1]=$!
trap INT " kill ${pid[0]} ${pid[1]}; exit 1"
wait
我会从这个模型开始,而不是使用bash作业控制(bg,fg,jobs)。通常,init继承并获取孤立进程。你想解决什么问题?
答案 2 :(得分:5)
阅读本文:How to send a signal SIGINT from script to script ? BASH
同样来自info bash
To facilitate the implementation of the user interface to job control,
the operating system maintains the notion of a current terminal process
group ID. Members of this process group (processes whose process group
ID is equal to the current terminal process group ID) receive keyboard-
generated signals such as SIGINT. These processes are said to be in
the foreground. Background processes are those whose process group ID
differs from the terminal's; such processes are immune to keyboard-gen‐
erated signals.
因此bash
通过进程组ID 区分后台进程与前台进程。如果进程组标识等于进程标识,则该进程是前台进程,并在收到SIGINT
信号时终止。否则它将不会终止(除非它被困)。
您可以使用
查看流程组IDps x -o "%p %r %y %x %c "
因此,当您在脚本中运行后台进程(使用&
)时,它将忽略SIGINT
信号,除非它被捕获。
但是,您仍然可以使用其他信号来终止子进程,例如SIGKILL
,SIGTERM
等。
例如,如果您将脚本更改为以下内容,则会成功终止子进程:
#!/bin/bash
if [ "$1" = "--child" ]; then
sleep 1000
elif [ "$1" = "--parent" ]; then
"$0" --child &
for child in $(jobs -p); do
echo kill "$child" && kill "$child"
done
wait $(jobs -p)
else
echo "Must be invoked with --child or --parent."
fi
输出:
$ ./test.sh --parent
kill 2187
./test.sh: line 10: 2187 Terminated "$0" --child
答案 3 :(得分:1)
您可以继续使用SIGINT
后台任务,只需轻轻一点:将异步子流程调用放在函数或{
}
中,然后将其setsid
赋予它它有自己的流程组。
在这里,你的剧本保留了它的第一个意图:
使用和传播SIGINT
而不使用其他信号
仅修改从"$0" --child &
到{ setsid "$0" --child; } &
的调用
添加获取子实例的PID所需的代码,这是后台子shell中的唯一进程。
这是您的代码:
#!/bin/bash
if [ "$1" = "--child" ]; then
sleep 1000
elif [ "$1" = "--parent" ]; then
{ setsid "$0" --child; } &
subshell_pid=$!
pids=$(ps -ax -o ppid,pid --no-headers |
sed -r 's/^ +//g;s/ +/ /g' |
grep "^$subshell_pid " | cut -f 2 -d " ");
for child in $pids; do
echo kill -SIGINT "-$child" && kill -SIGINT "-$child"
done
wait $subshell_pid
else
echo "Must be invoked with --child or --parent."
以下是bash手册
中重要的文档部分处理组ID对后台进程的影响(在doc的作业控制部分中):
[...]进程组ID等于当前终端的进程 进程组ID [..]接收键盘生成的信号,如 SIGINT。据说这些过程处于前景。 后台进程是进程组ID不同的进程 终端的;这样的过程不受键盘生成的影响 信号强>
SIGINT
和SIGQUIT
的默认处理程序(在doc的信号部分):
bash运行的非内置命令将信号处理程序设置为shell从其父级继承的值。当作业控制不起作用时,除了这些继承的处理程序之外,异步命令会忽略SIGINT和SIGQUIT 。
关于陷阱的修改(在trap
内置文档中):
进入shell时忽略的信号无法被捕获或重置。