bash脚本如何对后台任务执行与Ctrl-C等效的操作?

bash脚本如何对后台任务执行与Ctrl-C等效的操作?,bash,Bash,是否有任何方法可以调用子进程,从而向它及其所有子进程发送一个中断,就像在前台任务中按Ctrl-C键一样?我正试图杀死一个调用长时间运行的子进程的启动程序脚本。我尝试了kill-SIGINT$child(它不会将中断发送给它的后代,因此是不可操作的)和kill-SIGINT-$child(它在交互调用时有效,但在脚本中运行时无效) 这是一个测试脚本。长时间运行的脚本是test.sh--child。当您调用test.sh--parent时,它调用test.sh--child&,然后试图杀死它。我怎样

是否有任何方法可以调用子进程,从而向它及其所有子进程发送一个中断,就像在前台任务中按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
信号,将它们发送到其子进程,然后等待(从 ),但有没有办法不修改子脚本

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."