2013-02-04 38 views
7

有什麼辦法來調用一個子進程,以便它和它的所有後代發送一箇中斷,就像你Ctrl-C的前臺任務一樣嗎?我試圖殺死一個調用長時間運行的孩子的啓動腳本。我試過kill -SIGINT $child(它不會將中斷髮送給它的後代,所以是沒有操作的)和kill -SIGINT -$child(它在交互式調用時工作,但在腳本中運行時不起作用)。bash腳本如何使Ctrl-C等同於後臺任務?

這是一個測試腳本。長時間運行的腳本是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 script kill background (grand)children on Ctrl+C),但有沒有修改的孩子腳本什麼辦法?

回答

4

閱讀本:How to send a signal SIGINT from script to script ? 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從前臺進程區分後臺進程。如果進程組編號等於進程編號,則該進程是前臺進程,並且將在其接收到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 
+0

已更新的答案。 – user000001

5
somecommand & 

返回子的PID在$!

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

我想這個模型,而不是使用bash作業控制(BG,FG,作業)開始。通常init會繼承並收穫孤立進程。你想解決什麼問題?

+0

我意識到我可以修改子腳本來將中斷傳遞給它所產生的進程。我只是想知道是否可以在不修改子腳本的情況下將中斷髮送給後代。 – yonran

+1

供參考:2號線應該有一個$!不是$ 1 – Tully

+0

INT沒有爲我工作,我使用EXIT而不是OS X – Michal

8

大家很奇怪,你這是怎麼開展的孩子的在背景並殺死他們ctrl + c:

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

這很有用,雖然我發現'kill'只會殺死在參數中指定的進程,而不會殺死它們的後代。 – Flimm

0

你可以繼續使用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效果(在作業控制文檔的部分):

[...]處理W¯¯軟管進程組ID等於當前終端 進程組ID [..]接收鍵盤產生的信號,例如 SIGINT。據說這些過程處於前臺。 後臺進程是那些進程組ID從 終端的不同;這樣的工藝是免疫的鍵盤產生 信號

默認處理程序SIGINTSIGQUIT(在信號DOC的部分):

通過bash的運行

非內置命令有設定爲通過從它的父殼繼承的值的信號處理程序。當作業控制不起作用時,除了這些繼承的處理程序之外,異步命令還會忽略SIGINT和SIGQUIT

和約陷阱的變形例(在trap內建DOC):

信號在進入忽略到外殼不能被捕獲或復位