bash脚本怎样才能使Ctrl-C等同于后台任务?

有没有办法调用一个subprocess,以便它和它的所有后代发送一个中断,就像Ctrl-C是一个前台任务一样? 我试图杀死一个启动脚本,调用一个长期运行的孩子。 我已经试过kill -SIGINT $child (它不会将中断发送给它的后代,所以是一个no-op)并kill -SIGINT -$child (交互式调用时工作,但不在脚本中运行时)。

这是一个testing脚本。 长时间运行的脚本是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信号,发送到它的subprocess,然后等待(从Bash脚本杀死Ctrl + C的背景(大)儿童 ),但有没有办法不修改子脚本?

阅读本文: 如何发送一个信号SIGINT从脚本到脚本? 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来区分后台进程和前台进程。 如果进程组ID等于进程ID ,则该进程是前台进程,并在接收到SIGINT信号时终止。 否则它不会终止(除非被困住)。

您可以看到进程组标识

 ps x -o "%p %r %y %x %c " 

因此,当你在一个脚本中运行一个后台进程(用& )时,它将忽略SIGINT信号,除非被捕获。

但是,您仍然可以使用其他信号(例如SIGKILLSIGTERM等)来SIGKILLsubprocess。

例如,如果将脚本更改为以下,则会成功终止subprocess:

 #!/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 

对于任何人想知道,这是你如何在后台启动孩子,并杀死他们在CTRL + C:

 #!/usr/bin/env bash command1 & pid[0]=$! command2 & pid[1]=$! trap "kill ${pid[0]} ${pid[1]}; exit 1" INT wait 
 somecommand & 

$!返回一个孩子的pid $!

 somecommand & pid[0]=$! anothercommand & pid[1]=$! trap INT " kill ${pid[0]} ${pid[1]}; exit 1" wait 

我会从这个模型开始,而不是使用bash作业控制(bg,fg,jobs)。 通常init会inheritance并收获孤立进程。 你想解决什么问题?

你可以继续使用SIGINT处理背景任务,只需要简单的一些小事:把你的asynchronoussubprocess调用放到一个函数中,或者给它一个setsid ,使它拥有自己的进程组。

这是你的脚本,它的整个第一意图:

  • 使用和传播SIGINT而不使用其他信号

  • 只修改调用: "$0" --child & to { setsid "$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等于当前terminal进程组ID […]的进程接收键盘生成的信号,例如SIGINT。 据说这些过程处于前台。 后台进程是那些进程组ID与terminal不同的进程; 这样的过程不受键盘产生的信号的影响

SIGINTSIGQUIT默认处理程序(在doc的Signals部分):

由bash运行的非内build命令将信号处理程序设置为shell从其父项inheritance的值。 当作业控制无效时,除了这些inheritance的处理程序外, asynchronous命令还会忽略SIGINT和SIGQUIT

和关于陷阱的修改( trap内置文档):

进入shell时忽略的信号不能被捕获或重置