[英]How can bash script do the equivalent of Ctrl-C to a background task?
Is there any way to invoke a subprocess so that it and all its descendants are sent an interrupt, just as if you Ctrl-C a foreground task?有没有办法调用一个子进程,以便它和它的所有后代都被发送一个中断,就像你 Ctrl-C 一个前台任务一样? I'm trying to kill a launcher script that invokes a long-running child.
我试图杀死一个调用长期运行的孩子的启动器脚本。 I've tried
kill -SIGINT $child
(which doesn't send the interrupt to its descendants so is a no-op) and kill -SIGINT -$child
(which works when invoked interactively but not when running in a script).我试过
kill -SIGINT $child
(它不会将中断发送给它的后代,所以是一个无操作)和kill -SIGINT -$child
(它在交互调用时有效,但在脚本中运行时无效)。
Here's a test script.这是一个测试脚本。 The long-running script is
test.sh --child
.长时间运行的脚本是
test.sh --child
。 When you call test.sh --parent
, it invokes test.sh --child &
and then tries to kill it.当您调用
test.sh --parent
,它会调用test.sh --child &
然后尝试杀死它。 How can I make the parent kill the child successfully?我怎样才能让父母成功杀死孩子?
#!/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
I know that you can modify the long-running child to trap
signals, send them to its subprocess, and then wait (from Bash script kill background (grand)children on Ctrl+C ), but is there any way without modifying the child script?我知道你可以修改长时间运行的子进程来
trap
信号,将它们发送到它的子进程,然后等待(从Bash 脚本杀死后台(大)子进程在 Ctrl+C 上),但是有没有办法不修改子脚本?
For anyone wondering, this is how you launch childs in the background and kill them on ctrl+c:对于任何想知道的人,这就是您如何在后台启动孩子并在 ctrl+c 上杀死他们的方法:
#!/usr/bin/env bash
command1 &
pid[0]=$!
command2 &
pid[1]=$!
trap "kill ${pid[0]} ${pid[1]}; exit 1" INT
wait
somecommand &
returns a pid of the child in $!
以
$!
返回子进程的 pid
somecommand &
pid[0]=$!
anothercommand &
pid[1]=$!
trap "kill ${pid[0]} ${pid[1]}; exit 1" INT
wait
I would start with this model rather than with bash job control (bg, fg, jobs).我将从这个模型开始,而不是从 bash 作业控制(bg、fg、jobs)开始。 Normally init inherits and reaps orphan processes.
通常 init 会继承和收割孤儿进程。 What problem are you trying to solve?
你想解决什么问题?
Read this : How to send a signal SIGINT from script to script ?阅读: 如何从脚本发送信号 SIGINT 到脚本? BASH
巴什
Also from info 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.
So bash
differentiates background processes from foreground processes by the process group ID .因此
bash
通过进程组 ID区分后台进程和前台进程。 If the process group id is equal to process id , then the process is a foreground process, and will terminate when it receives a SIGINT
signal.如果进程组 id等于进程 id ,则该进程是前台进程,并在收到
SIGINT
信号时终止。 Otherwise it will not terminate (unless it is trapped).否则它不会终止(除非它被困)。
You can see the process group Id with您可以看到进程组 ID
ps x -o "%p %r %y %x %c "
Thus, when you run a background process (with &
) from within a script, it will ignore the SIGINT
signal, unless it is trapped.因此,当您从脚本中运行后台进程(使用
&
)时,它将忽略SIGINT
信号,除非它被捕获。
However, you can still kill the child process with other signals, such as SIGKILL
, SIGTERM
, etc.但是,您仍然可以使用其他信号杀死子进程,例如
SIGKILL
、 SIGTERM
等。
For example, if you change your script to the following it will successfully kill the child process:例如,如果您将脚本更改为以下内容,它将成功终止子进程:
#!/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
Output:输出:
$ ./test.sh --parent
kill 2187
./test.sh: line 10: 2187 Terminated "$0" --child
You can keep using SIGINT
with background tasks with an easy little twist: Put your asynchronous subprocess call in a function or {
}
, and give it setsid
so it has its own process group.您可以通过一个简单的小
SIGINT
继续将SIGINT
用于后台任务:将您的异步子进程调用放在一个函数或{
}
,并为其提供setsid
以便它拥有自己的进程组。
Here's your script keep it's whole first intention:这是您的脚本,保持其全部初衷:
using and propagating SIGINT
and not using another signal使用和传播
SIGINT
而不是使用其他信号
modifying only the calling from: "$0" --child &
to { setsid "$0" --child; } &
只修改调用:
"$0" --child &
到{ setsid "$0" --child; } &
"$0" --child &
{ setsid "$0" --child; } &
{ setsid "$0" --child; } &
adding the code necessary to get the PID of your child instance, which is the only process in the background subshell.添加获取子实例PID所需的代码,这是后台子shell中的唯一进程。
Here's your code:这是你的代码:
#!/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."
Here's the important doc part from bash manual这是 bash 手册中重要的文档部分
Process group id effect on background process (in Job Control section of doc):进程组 id 对后台进程的影响(在文档的作业控制部分):
[...] processes whose process group ID is equal to the current terminal process group ID [..] receive keyboard-generated signals such as SIGINT.
[...] 进程组 ID 等于当前终端进程组 ID [..] 的进程接收键盘生成的信号,例如 SIGINT。 These processes are said to be in the foreground.
据说这些进程在前台。 Background processes are those whose process group ID differs from the terminal's;
后台进程是那些进程组 ID 与终端不同的进程; such processes are immune to keyboard-generated signals .
此类进程不受键盘生成信号的影响。
Default handler for SIGINT
and SIGQUIT
(in Signals section of doc): SIGINT
和SIGQUIT
默认处理程序(在文档的信号部分):
Non-builtin commands run by bash have signal handlers set to the values inherited by the shell from its parent.
bash 运行的非内置命令将信号处理程序设置为 shell 从其父级继承的值。 When job control is not in effect, asynchronous commands ignore SIGINT and SIGQUIT in addition to these inherited handlers.
当作业控制无效时,除了这些继承的处理程序之外,异步命令还会忽略 SIGINT 和 SIGQUIT 。
and about modification of traps (in trap
builtin doc):以及关于陷阱的修改(在
trap
内置文档中):
Signals ignored upon entry to the shell cannot be trapped or reset .
进入外壳时被忽略的信号不能被捕获或重置。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.