bash脚本如何对后台任务执行与Ctrl-C等效的操作?
是否有任何方法可以调用子进程,从而向它及其所有子进程发送一个中断,就像在前台任务中按Ctrl-C键一样?我正试图杀死一个调用长时间运行的子进程的启动程序脚本。我尝试了bash脚本如何对后台任务执行与Ctrl-C等效的操作?,bash,Bash,是否有任何方法可以调用子进程,从而向它及其所有子进程发送一个中断,就像在前台任务中按Ctrl-C键一样?我正试图杀死一个调用长时间运行的子进程的启动程序脚本。我尝试了kill-SIGINT$child(它不会将中断发送给它的后代,因此是不可操作的)和kill-SIGINT-$child(它在交互调用时有效,但在脚本中运行时无效) 这是一个测试脚本。长时间运行的脚本是test.sh--child。当您调用test.sh--parent时,它调用test.sh--child&,然后试图杀死它。我怎样
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
信号,将它们发送到其子进程,然后等待(从
),但有没有办法不修改子脚本
somecommand &
返回$中子项的pid代码>
somecommand &
pid[0]=$!
anothercommand &
pid[1]=$!
trap "kill ${pid[0]} ${pid[1]}; exit 1" INT
wait
我将从这个模型开始,而不是bash作业控制(bg、fg、jobs)。通常init继承并获取孤立进程。你想解决什么问题?阅读以下内容:
同样来自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区分后台进程和前台进程。如果进程组ID等于进程ID,则该进程为前台进程,并将在收到SIGINT
信号时终止。否则它将不会终止(除非它被捕获)
您可以通过以下方式查看进程组Id:
因此,当您从脚本中运行后台进程(使用&
)时,它将忽略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
对于任何想知道的人来说,这就是如何在后台启动child并在ctrl+c上杀死它们:
#!/usr/bin/env bash
command1 &
pid[0]=$!
command2 &
pid[1]=$!
trap "kill ${pid[0]} ${pid[1]}; exit 1" INT
wait
您可以在后台任务中继续使用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对后台流程的影响(在单据的作业控制部分):
[…]进程组ID等于当前终端的进程
进程组ID[…]接收键盘生成的信号,如
信号。据说这些过程是有前景的。
后台进程是进程组ID与
航站楼的;这些过程不受键盘生成的影响
信号
SIGINT
和SIGQUIT
的默认处理程序(在文档的信号部分):
bash运行的非内置命令将信号处理程序设置为shell从其父级继承的值。当作业控制无效时,异步命令除了这些继承的处理程序外,还会忽略SIGINT和SIGQUIT
关于陷阱的修改(在trap
内置文档中):
进入外壳时忽略的信号无法捕获或重置
我意识到我可以修改子脚本,将中断传递给它生成的进程。我只是想知道是否可以在不修改子脚本的情况下向后代发送中断。仅供参考:第2行应该有$!不是$1INT对我不起作用,我在OS X上使用了EXIT,应该是
trap“kill…”INT
,而不是trap INT“kill…”
?这很有用,尽管我发现kill
只会杀死参数中指定的进程,而不会杀死它们的后代。
#!/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."