[英]How can bash script do the equivalent of Ctrl-C to a background task?
有沒有辦法調用一個子進程,以便它和它的所有后代都被發送一個中斷,就像你 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
信號,將它們發送到它的子進程,然后等待(從Bash 腳本殺死后台(大)子進程在 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 &
以$!
返回子進程的 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
ps x -o "%p %r %y %x %c "
因此,當您從腳本中運行后台進程(使用&
)時,它將忽略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
您可以通過一個簡單的小SIGINT
繼續將SIGINT
用於后台任務:將您的異步子進程調用放在一個函數或{
}
,並為其提供setsid
以便它擁有自己的進程組。
這是您的腳本,保持其全部初衷:
使用和傳播SIGINT
而不是使用其他信號
只修改調用: "$0" --child &
到{ setsid "$0" --child; } &
"$0" --child &
{ 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 對后台進程的影響(在文檔的作業控制部分):
[...] 進程組 ID 等於當前終端進程組 ID [..] 的進程接收鍵盤生成的信號,例如 SIGINT。 據說這些進程在前台。 后台進程是那些進程組 ID 與終端不同的進程; 此類進程不受鍵盤生成信號的影響。
SIGINT
和SIGQUIT
默認處理程序(在文檔的信號部分):
bash 運行的非內置命令將信號處理程序設置為 shell 從其父級繼承的值。 當作業控制無效時,除了這些繼承的處理程序之外,異步命令還會忽略 SIGINT 和 SIGQUIT 。
以及關於陷阱的修改(在trap
內置文檔中):
進入外殼時被忽略的信號不能被捕獲或重置。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.