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
信号,除非被捕获。
但是,您仍然可以使用其他信号(例如SIGKILL
, SIGTERM
等)来SIGKILL
subprocess。
例如,如果将脚本更改为以下,则会成功终止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不同的进程; 这样的过程不受键盘产生的信号的影响 。
SIGINT
和SIGQUIT
默认处理程序(在doc的Signals部分):
由bash运行的非内build命令将信号处理程序设置为shell从其父项inheritance的值。 当作业控制无效时,除了这些inheritance的处理程序外, asynchronous命令还会忽略SIGINT和SIGQUIT 。
和关于陷阱的修改( trap
内置文档):
进入shell时忽略的信号不能被捕获或重置 。