プロセスグループのすべてのメンバーにシグナルを送信する最良の方法は何ですか?


422

プロセスツリー全体を強制終了したい。一般的なスクリプト言語を使用してこれを行う最良の方法は何ですか?簡単な解決策を探しています。


3
システムリーパーが実行されても、ゾンビは消えるはずです。ゾンビが残っているシステムを見たことは認めますが、それは典型的ではありません。
Brian Knoblauch、

7
時々、それらのゾンビが恐ろしい活動の原因となっていることがあります。
User1

chronosまたはherodesコマンドのいずれかを使用します。
Michael Le BarbierGrünewald14年

8
kill $(pstree <PID> -p -a -l | cut -d、-f2 | cut -d '' -f1)
vrdhn

@MichaelLeBarbierGrünewaldこれらのプログラムにリンクしていただけませんか?
発泡スチロールのフライ

回答:


305

あなたが殺したいツリーが単一のプロセスグループであるかどうかは言いません。(これは、ツリーがサーバーの起動またはシェルのコマンドラインからの分岐の結果である場合によく見られます。)以下のように、GNU psを使用してプロセスグループを検出できます。

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

強制終了したいプロセスグループの場合は、kill(1)コマンドを使用しますが、プロセス番号を与える代わりに、グループ番号の否定を与えます。たとえば、グループ5112のすべてのプロセスを強制終了するには、を使用しますkill -TERM -- -5112


3
kill -74313 -bash:kill:74313:無効な信号指定kill -15 -GPIDを追加すると、完全に機能しました。
Adam Peck

51
ほとんどすべてのコマンドで通常どおり、-で始まる通常の引数がスイッチとして解釈されないようにするには、その前に-:kill--GPID
ysth

9
pgrepプロセスグループIDを見つける簡単な方法を提供できます。たとえば、my-script.shのプロセスグループを強制終了するには、を実行しkill -TERM -$(pgrep -o my-script.sh)ます。
ジョシュケリー

9
でより良い外観stackoverflow.com/questions/392022/...そのはるかによりエレガントなソリューション、あなたが子供のPIDを一覧表示する必要がある場合は、次に使用:ps -o pid --no-headers --ppid $PARENT_PID
のSzymonジェズ

4
そして、あなたは少し、ソート形式を変更した場合、あなたは(潜在的に)すべてのプロセスがうまくまとめられ、始まる見るために、各グループのグループの親を取得:ps x -o "%r %p %y %x %c" | sort -nk1,2
haridsv

199

プロセスグループID()を使用して、同じプロセスツリーに属するすべてのプロセスを強制終了しますPGID

  • kill -- -$PGID     デフォルトの信号を使用(TERM= 15)
  • kill -9 -$PGID     信号を使用するKILL(9)

同じプロセスツリーのPGID任意のプロセスIDPID)からを取得できます

  • kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*')   (信号TERM
  • kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*')   (信号KILL

感謝フウキンチョウ科Speakusの貢献のため$PIDの残りのスペースとOSXの互換性。

説明

  • kill -9 -"$PGID"=> KILLすべての子供と孫に信号9()を送信...
  • PGID=$(ps opgid= "$PID")=> Process-Parent-IDだけでなく、ツリーの任意のProcess -IDからProcess-Group-IDを取得します。のバリエーションは、で置き換えることができる場所です。だが: ps opgid= $PIDps -o pgid --no-headers $PIDpgidpgrp
    • psPID5桁未満で、tanagerが気づくように右揃えになっている場合、先行スペースを挿入します。以下を使用できます。
      PGID=$(ps opgid= "$PID" | tr -d ' ')
    • psOSXからは常にヘッダーを出力するため、Speakus以下を提案します。
      PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
  • grep -o [0-9]* 連続する数字のみを出力します(スペースやアルファベット順のヘッダーは出力しません)。

その他のコマンドライン

PGID=$(ps -o pgid= $PID | grep -o [0-9]*)
kill -TERM -"$PGID"  # kill -15
kill -INT  -"$PGID"  # correspond to [CRTL+C] from keyboard
kill -QUIT -"$PGID"  # correspond to [CRTL+\] from keyboard
kill -CONT -"$PGID"  # restart a stopped process (above signals do not kill it)
sleep 2              # wait terminate process (more time if required)
kill -KILL -"$PGID"  # kill -9 if it does not intercept signals (or buggy)

制限

  • 気づいたようダヴィデヒューバートKarioとき、kill同じ木に属するプロセスによって呼び出され、killツリー全体の殺害を終了する前に自分自身を殺すためにリスク。
  • したがって、異なるProcess-Group-IDを持つプロセスを使用してコマンドを実行してください。

長い話

> cat run-many-processes.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./child.sh background &
./child.sh foreground
echo "ProcessID=$$ ends ($0)"

> cat child.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./grandchild.sh background &
./grandchild.sh foreground
echo "ProcessID=$$ ends ($0)"

> cat grandchild.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
sleep 9999
echo "ProcessID=$$ ends ($0)"

'&'を使用してバックグラウンドでプロセスツリーを実行する

> ./run-many-processes.sh &    
ProcessID=28957 begins (./run-many-processes.sh)
ProcessID=28959 begins (./child.sh)
ProcessID=28958 begins (./child.sh)
ProcessID=28960 begins (./grandchild.sh)
ProcessID=28961 begins (./grandchild.sh)
ProcessID=28962 begins (./grandchild.sh)
ProcessID=28963 begins (./grandchild.sh)

> PID=$!                    # get the Parent Process ID
> PGID=$(ps opgid= "$PID")  # get the Process Group ID

> ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
28348 28349 28349 28349 pts/3    28969 Ss   33021   0:00 -bash
28349 28957 28957 28349 pts/3    28969 S    33021   0:00  \_ /bin/sh ./run-many-processes.sh
28957 28958 28957 28349 pts/3    28969 S    33021   0:00  |   \_ /bin/sh ./child.sh background
28958 28961 28957 28349 pts/3    28969 S    33021   0:00  |   |   \_ /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3    28969 S    33021   0:00  |   |   |   \_ sleep 9999
28958 28963 28957 28349 pts/3    28969 S    33021   0:00  |   |   \_ /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3    28969 S    33021   0:00  |   |       \_ sleep 9999
28957 28959 28957 28349 pts/3    28969 S    33021   0:00  |   \_ /bin/sh ./child.sh foreground
28959 28960 28957 28349 pts/3    28969 S    33021   0:00  |       \_ /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3    28969 S    33021   0:00  |       |   \_ sleep 9999
28959 28962 28957 28349 pts/3    28969 S    33021   0:00  |       \_ /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3    28969 S    33021   0:00  |           \_ sleep 9999
28349 28969 28969 28349 pts/3    28969 R+   33021   0:00  \_ ps fj

コマンドpkill -P $PIDは孫を殺しません:

> pkill -P "$PID"
./run-many-processes.sh: line 4: 28958 Terminated              ./child.sh background
./run-many-processes.sh: line 4: 28959 Terminated              ./child.sh foreground
ProcessID=28957 ends (./run-many-processes.sh)
[1]+  Done                    ./run-many-processes.sh

> ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
28348 28349 28349 28349 pts/3    28987 Ss   33021   0:00 -bash
28349 28987 28987 28349 pts/3    28987 R+   33021   0:00  \_ ps fj
    1 28963 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3    28987 S    33021   0:00  \_ sleep 9999
    1 28962 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3    28987 S    33021   0:00  \_ sleep 9999
    1 28961 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3    28987 S    33021   0:00  \_ sleep 9999
    1 28960 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3    28987 S    33021   0:00  \_ sleep 9999

このコマンドkill -- -$PGIDは、孫を含むすべてのプロセスを強制終了します。

> kill --    -"$PGID"  # default signal is TERM (kill -15)
> kill -CONT -"$PGID"  # awake stopped processes
> kill -KILL -"$PGID"  # kill -9 to be sure

> ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
28348 28349 28349 28349 pts/3    29039 Ss   33021   0:00 -bash
28349 29039 29039 28349 pts/3    29039 R+   33021   0:00  \_ ps fj

結論

私は、この例で通知PIDし、PGID(等しいです28957)。
元々kill -- -$PID十分だと思ったのはこのためです。しかし、場合には、プロセスが内部に産卵されたプロセスIDが異なっているグループのIDMakefile

kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)は、別のグループID(別のプロセスツリー)から呼び出されたときに、プロセスツリー全体を強制終了する最も簡単な方法だと思います。


1
@davideさん、こんにちは。良い質問。自分killのシグナルを受信する前に、常にツリー全体にシグナルを送信する必要があると思います。ただし、特定の状況や実装によってkillは、信号を自分自身に送信し、割り込みを受けてから、独自の信号を受信する場合があります。ただし、リスクは十分に最小限である必要があり、このバグの前に他のバグが発生するため、ほとんどの場合無視できます。あなたの場合、このリスクは無視できますか?さらに、他の回答にはこの一般的なバグkillがあります(プロセスツリーの一部が強制終了されます)。この助けを願っています..乾杯;)
olibre 2013年

3
これは、サブコマンド自体がグループリーダーにならない場合にのみ機能します。そのような単純なツールでさえ、manそうします。一方、gandchildプロセスを子プロセスから強制終了したい場合kill -- -$pidは機能しません。したがって、これは一般的なソリューションではありません。
Hubert Kario 2013年

1
例は、子を殺そうとする「子」です(ユーザーが開始したコマンドの孫)。IOW、でバックグラウンドプロセス階層を強制終了してみてくださいchild.sh
Hubert Kario

1
> kill -QUIT-"$ PGID"#キーボードからの[CRTL + C]と同じ信号---- QUITをINTに置き換えると真になる
Maxim Kholyavkin '27 / 02/27

1
OSXオプション--no-headersはサポートされていないため、コードを次のように更新する必要があります:PGID = "$(ps -o pgid" $ PID "| grep [0-9] | tr -d '')"
Maxim Kholyavkin

166
pkill -TERM -P 27888

これにより、親プロセスID 27888を持つすべてのプロセスが強制終了されます。

またはより堅牢:

CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS

33秒後にkillをスケジュールし、プロセスに終了を丁寧に要求します。

すべての子孫を終了するには、この回答を参照してください。


19
私の簡単なテストでは、pgrepは直接の子のみを報告したため、これは階層全体を殺すことはできません。
haridsv

10
私は@haridsvに同意します:pkill -P子にのみ信号を送信します=>孫は信号を受信しません=>したがって、私はそれを説明するために別の回答を書いています。乾杯;-)
olibre 2013

1
bashスクリプトから、自分の子供を殺すには、を使用しますpkill -TERM -P ${$}
フランソワボーソレイユ2015

3
@Onlyjob、眠ってから殺すのは危険ではないですか?その間、プロセスIDはOSによって再利用された可能性があります。あなたはもう子供ではないプロセスを強制終了している可能性があります。これを確実にするには、pkillの呼び出しをもう一度行う必要があると思います。
フランソワボーソレイユ2015

2
@Onlyjob FYI、私は自分のマシンで19秒未満の軽いI / Oアクセスで32768プロセス、シングルスレッドを生成することができます:$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
cychoi

102

プロセスツリーを再帰的に強制終了するには、killtree()を使用します。

#!/bin/bash

killtree() {
    local _pid=$1
    local _sig=${2:--TERM}
    kill -stop ${_pid} # needed to stop quickly forking parent from producing children between child killing and parent killing
    for _child in $(ps -o pid --no-headers --ppid ${_pid}); do
        killtree ${_child} ${_sig}
    done
    kill -${_sig} ${_pid}
}

if [ $# -eq 0 -o $# -gt 2 ]; then
    echo "Usage: $(basename $0) <pid> [signal]"
    exit 1
fi

killtree $@

3
--引数がするps、それが置き換えそこ動作させるOS Xに入っていない仕事をするps:して、コマンドを前に定義されている:ループps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g_regexforlocal _regex="[ ]*([0-9]+)[ ]+${_pid}"
アルトゥル

5
停止したプロセスはSIGTERMで強制終了されません。私の答えを
x-yuri

1
-1は#!/ usr / bin / env bashの代わりに#!/ bin / bashを使用します(またはPOSIXのみの構成と/ bin / sh)
Good Person

killtree()確実に機能することを保証するために、SIGTERMの代わりにSIGKILLを送信するだけで十分でしょうか?
デビッド2013年

3
psがサポートしていない場合は--ppidpgrep -P ${_pid}代わりに使用できます
Hubert

16

rkillからのコマンド pslistパッケージには、信号(または特定の送信SIGTERM指定されたプロセスとそのすべての子孫にデフォルトで):

rkill [-SIG] pid/name...

11

ブラッドの答えも私がお勧めしたいことですがawk--ppidオプションを使用すると、完全に排除できますps

for child in $(ps -o pid -ax --ppid $PPID) do ....... done

これは、何らかの理由で-axを取り出さない限り機能しません(Centos5)。そうでなければこれは素晴らしいです!
xitrium

9

親プロセスのpidを渡すことがわかっている場合は、動作するシェルスクリプトを次に示します。

for child in $(ps -o pid,ppid -ax | \
   awk "{ if ( \$2 == $pid ) { print \$1 }}")
do
  echo "Killing child process $child because ppid = $pid"
  kill $child
done

psの一部のバージョンでは、「ax」の代わりに「-ax」を使用すると警告がスローされます。したがって:$(ps -o pid、ppid ax | \ awk "{if(\ $ 2 == $ pid){print \ $ 1}}")の子の場合
Cory R. King

9

ここで説明されている方法の少し変更されたバージョンを使用します:https : //stackoverflow.com/a/5311362/563175

だからそれはそのように見えます:

kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "`

24901は親のPIDです。

見た目はかなり醜いですが、完璧に機能します。


1
sedの代わりにgrepで簡素化...pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
anishsane

2
に追加-lする必要がpstreeあるので、長い行は切り捨てられません。読みやすくすることもできますkill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`\n正常に機能するため、スペースに変換する必要はありません)。
Aquarius Power

9

zhigangの答えの修正版:

#!/usr/bin/env bash
set -eu

killtree() {
    local pid
    for pid; do
        kill -stop $pid
        local cpid
        for cpid in $(pgrep -P $pid); do
            killtree $cpid
        done
        kill $pid
        kill -cont $pid
        wait $pid 2>/dev/null || true
   done
}

cpids() {
    local pid=$1 options=${2:-} space=${3:-}
    local cpid
    for cpid in $(pgrep -P $pid); do
        echo "$space$cpid"
        if [[ "${options/a/}" != "$options" ]]; then
            cpids $cpid "$options" "$space  "
        fi
    done
}

while true; do sleep 1; done &
cpid=$!
for i in $(seq 1 2); do
    cpids $$ a
    sleep 1
done
killtree $cpid
echo ---
cpids $$ a

wait $pidすべてのプロセスではなく、開始したプロセスでのみ実行できるため、これは一般的な解決策ではありません
Hubert Kario 2014年

@Hubert Karioその場合、待機はゼロ以外のステータスで終了し、スクリプトの実行を続行します。私が間違っている?ただし、子の場合waitTerminatedメッセージを抑制します。
x-yuri

9

コメントできない(評判が足りない)ので、新しい回答を追加せざるを得ない、実際には回答ではありませんがません。

2月28日に@olibreによって出された、それ以外の場合は非常に素晴らしく完全な回答には、わずかな問題があります。の出力にps opgid= $PIDps、列を正当化するため、5桁より短いPIDの先行スペースが含まれます(数値を厳密に揃えます)。コマンドライン全体で、これは負の記号、スペース、グループPIDの順になります。単純な解決策は、パイプpsを使用trしてスペースを削除することです。

kill -- -$( ps opgid= $PID | tr -d ' ' )

ありがとうございます。私は私の答えを修正します;)乾杯
olibre 2013年

8

Norman Ramseyの回答に追加するには、プロセスグループを作成する場合は、setsidを確認する価値があります。
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html

呼び出しプロセスがプロセスグループリーダーでない場合、setsid()関数は新しいセッションを作成します。戻ったとき、呼び出しプロセスは、この新しいセッションのセッションリーダーであり、新しいプロセスグループのプロセスグループリーダーであり、制御端末を持っていません。呼び出しプロセスのプロセスグループIDは、呼び出しプロセスのプロセスIDと等しく設定されます。呼び出しプロセスは、新しいプロセスグループの唯一のプロセスであり、新しいセッションの唯一のプロセスです。

つまり、開始プロセスからグループを作成できるということです。起動後にプロセスツリー全体を強制終了できるようにするために、これをphpで使用しました。

これは悪い考えかもしれません。コメントに興味があります。


実際、これは素晴らしいアイデアであり、非常にうまく機能します。同じプロセスグループにプロセスを配置できる場合(またはそれらがすでに同じグループにある場合)に使用しています。
sickill 2013年

7

ysthのコメントに触発されました

kill -- -PGID

プロセス番号を与える代わりに、グループ番号の否定を与えます。ほとんどすべてのコマンドで通常どおり、aで始まる通常の引数が-スイッチとして解釈されないようにするには、その前に--


おっと、私はあなたと同じ答えを出したと気づきました=> +1。しかし、さらに私は単にPGIDから得る方法を説明しますPID。どう思いますか?乾杯
olibre

5

次のシェル関数は他の多くの回答と似ていますが、LinuxとBSD(OS Xなど)の両方で機能しますpgrep

killtree() {
    local parent=$1 child
    for child in $(ps -o ppid= -o pid= | awk "\$1==$parent {print \$2}"); do
        killtree $child
    done
    kill $parent
}

2行目の最後に「こども」という単語があると思います。
mato

@mato –追加ではありません。$child同じ名前の他の(非ローカル)変数を妨害せず、関数の終了後にローカル変数の値が確実にクリーンアップされるように、その関数のスコープを制限します。
Adam Katz

ああ、なるほど、割り当ての一部だと思いました。私はコメントを書く前に(遅く)もう一度読むべきだと思います。:)ありがとう。
mato

ところで、子供たちのリストを作成してから、上(親)から殺害を開始する方が良いでしょうか?..したがって、親が子を再作成したり、意図した動作を変更する可能性のある次のコードの実行を継続したりする状況を回避できます。
mato

5

psutilを使用してpythonでこれを行うのは非常に簡単です。pipでpsutilをインストールするだけで、プロセス操作ツールの完全なスイートが得られます。

def killChildren(pid):
    parent = psutil.Process(pid)
    for child in parent.get_children(True):
        if child.is_running():
            child.terminate()

5

zhigangの答えに基づいて、これは自殺を回避します:

init_killtree() {
    local pid=$1 child

    for child in $(pgrep -P $pid); do
        init_killtree $child
    done
    [ $pid -ne $$ ] && kill -kill $pid
}

4

名前でプロセスを強制終了したい場合:

killall -9 -g someprocessname

または

pgrep someprocessname | xargs pkill -9 -g

3

これは、bashスクリプトを使用してすべての子プロセスを強制終了する私のバージョンです。再帰は使用せず、pgrepコマンドに依存します。

使用する

killtree.sh PID SIGNAL

killtrees.shの内容

#!/bin/bash
PID=$1
if [ -z $PID ];
then
    echo "No pid specified"
fi

PPLIST=$PID
CHILD_LIST=`pgrep -P $PPLIST -d,`

while [ ! -z "$CHILD_LIST" ]
do
    PPLIST="$PPLIST,$CHILD_LIST"
    CHILD_LIST=`pgrep -P $CHILD_LIST -d,`
done

SIGNAL=$2

if [ -z $SIGNAL ]
then
    SIGNAL="TERM"
fi
#do substring from comma to space
kill -$SIGNAL ${PPLIST//,/ }

1
子リストが作成された後に新しいプロセスが作成された場合、これは失敗します。
ceving

3

@zhigangの回答のバリエーションで、AWKを使用せず、Bashのネイティブ解析の可能性のみに依存しています。

function killtree {
  kill -STOP "$1"
  ps -e -o pid= -o ppid= | while read -r pid ppid
                           do
                             [[ $ppid = $1 ]] || continue
                             killtree "$pid"  || true # Skip over failures
                           done
  kill -CONT "$1"          
  kill -TERM "$1"
}

MacとLinuxの両方で正常に動作するようです。複数の環境で構築する必要のあるソフトウェアをテストするためのスクリプトを作成する場合など、プロセスグループの管理に依存できない状況では、このツリーウォーキングテクニックは非常に役立ちます。


1

皆さんの知恵に感謝します。私のスクリプトは終了時にいくつかの子プロセスを残しており、否定のヒントによって物事が簡単になりました。この関数は、必要に応じて他のスクリプトで使用されるように作成しました。

# kill my group's subprocesses:          killGroup
# kill also myself:                      killGroup -x
# kill another group's subprocesses:     killGroup N  
# kill that group all:                   killGroup -x N
# N: PID of the main process (= process group ID).

function killGroup () {
    local prid mainpid
    case $1 in
        -x) [ -n "$2" ] && kill -9 -$2 || kill -9 -$$ ;;
        "") mainpid=$$ ;;
         *) mainpid=$1 ;;
    esac
    prid=$(ps ax -o pid,pgid | grep $mainpid)
    prid=${prid//$mainpid/}
    kill -9 $prid 2>/dev/null
    return
}

乾杯。


1

システムにpstreeとperlがある場合は、これを試すことができます:

perl -e 'kill 9, (`pstree -p PID` =~ m/\((\d+)\)/sg)'

1

子供より先に親を殺したほうがいいでしょう。そうしないと、親が殺される前に、親が新しい子を再び生成する可能性があります。これらは殺害を生き残るでしょう。

私のバージョンのpsは上記のものとは異なります。多分古すぎるかもしれないので、奇妙なぐちゃぐちゃ...

シェル関数の代わりにシェルスクリプトを使用することには多くの利点があります...

しかし、それは基本的にジガンのアイデアです


#!/bin/bash
if test $# -lt 1 ; then
    echo >&2 "usage: kiltree pid (sig)"
fi ;

_pid=$1
_sig=${2:-TERM}
_children=$(ps j | grep "^[ ]*${_pid} " | cut -c 7-11) ;
echo >&2 kill -${_sig} ${_pid}
kill -${_sig} ${_pid}
for _child in ${_children}; do
    killtree ${_child} ${_sig}
done

@zhighangスクリプトは親プロセスをSIGSTOPし、停止したプロセスにシグナルを配信するため、これは(AFAIK)子プロセスの作成とシグナル配信の間に競合状態を引き起こすべきではないことに注意してください。しかし、あなたのバージョンは、子供のリストを取得することと、親に信号を送ることとの間に競争があります。
Hubert Kario

1

以下はFreeBSD、Linux、MacOS Xでテストされており、pgrepとkillにのみ依存しています(ps -oバージョンはBSDでは機能しません)。最初の引数は、子を終了する必要がある親pidです。2番目の引数は、親pidも終了する必要があるかどうかを決定するブール値です。

KillChilds() {
        local pid="${1}"
        local self="${2:-false}"

        if children="$(pgrep -P "$pid")"; then
                for child in $children; do
                        KillChilds "$child" true
                done
        fi

        if [ "$self" == true ]; then
                kill -s SIGTERM "$pid" || (sleep 10 && kill -9 "$pid" &)
        fi
}

KillChilds $$ > /dev/null 2>&1

これはSIGTERMをシェルスクリプト内のすべての子/孫プロセスに送信し、SIGTERMが成功しなかった場合は、10秒待ってからkillを送信します。


以前の答え:

以下も機能しますが、BSDではシェル自体を強制終了します。

KillSubTree() {
    local parent="${1}"
    for child in $(ps -o pid=$parent); do
            if [ $$ -ne $child ]; then (kill -s SIGTERM $child || (sleep 10 && kill -9 $child & )) > /dev/null 2>&1 ; fi
    done
}
# Example lanch from within script
KillSubTree $$ > /dev/null 2>&1

1

私はzhigang、xyuri、solidsneckのソリューションをさらに開発します。

 #!/bin/bash

if test $# -lt 1 ; then
    echo >&2 "usage: kiltree pid (sig)"
    exit 1 ;
  fi ;

_pid=$1
_sig=${2:-TERM}

# echo >&2 "killtree($_pid) mypid = $$"
# ps axwwf | grep -6 "^[ ]*$_pid " >&2 ;

function _killtree () {
    local _children
    local _child
    local _success

    if test $1 -eq $2 ; then # this is killtree - don't commit suicide!
        echo >&2 "killtree can´t kill it´s own branch - some processes will survive." ; 
        return 1 ;
      fi ;
    # this avoids that children are spawned or disappear.
    kill -SIGSTOP $2 ;

    _children=$(ps -o pid --no-headers --ppid $2) ;        
    _success=0 
    for _child in ${_children}; do
        _killtree $1 ${_child} $3 ;
        _success=$(($_success+$?)) ;
      done ;

    if test $_success -eq 0 ; then
        kill -$3 $2
      fi ;
    # when a stopped process is killed, it will linger in the system until it is continued
    kill -SIGCONT $2
    test $_success -eq 0 ;
    return $?
    }

_killtree $$ $_pid $_sig

このバージョンは、その祖先を殺すことを避けます-以前のソリューションで子プロセスの洪水を引き起こします。

子リストが決定される前にプロセスが適切に停止されるため、新しい子が作成されたり消えたりすることはありません。

停止された後、停止したジョブはシステムから消え続けるために継続する必要があります。


1

古い質問ですが、すべての応答がpsを呼び出し続けているようです。

このawkベースのソリューションは再帰を必要とせず、psを1回だけ呼び出します。

awk 'BEGIN {
  p=1390
  while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2
  o=1
  while (o==1) {
    o=0
    split(p, q, " ")
    for (i in q) if (a[q[i]]!="") {
      p=p""a[q[i]]
      o=1
      a[q[i]]=""
    }
  }
  system("kill -TERM "p)
}'

または単一行で:

awk 'BEGIN {p=1390;while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2;o=1;while (o==1) {o=0;split(p, q, " ");for (i in q) {if (a[q[i]]!="") {p=p""a[q[i]];o=1;a[q[i]]=""}}}system("kill -TERM "p)}'

基本的には、parent:childエントリの配列(a)を作成し、配列をループして一致する親の子を見つけ、親リスト(p)に追加するという考え方です。

トップレベルのプロセスを強制終了したくない場合は、

sub(/[0-9]*/, "", p)

system()行がキルセットから削除する直前。

ここには競合状態があることを覚えておいてください。しかし、それは(私の知る限り)すべてのソリューションに当てはまります。私が必要とするスクリプトは、寿命の短い子供をたくさん作成しないので、私が必要とすることを行います。

リーダーの練習は、2パスループにすることです。最初のパスの後で、SIGSTOPをpリスト内のすべてのプロセスに送信し、次にループを実行してpsを再度実行し、2番目のパスの後にSIGTERMを送信してから、SIGCONTを送信します。素敵なエンディングを気にしないのなら、2パス目はSIGKILLだけかもしれません。


0

削除したいもののPIDがわかっている場合は、通常、セッションIDと、同じセッション内のすべてから移動できます。私は二重チェックしますが、私が死にたいループでrsyncを開始するスクリプトにこれを使用しました。

kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709))

pidがわからない場合でも、さらにネストできます

kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync )))


0

shでは、jobsコマンドはバックグラウンドプロセスを一覧表示します。古いプロセスが共有ソケットを作成した場合など、場合によっては、最新のプロセスを最初に強制終了する方が良い場合があります。これらの場合、PIDを逆の順序で並べ替えます。ジョブが停止する前に、ジョブがディスクなどに何かを書き込むのを待つ必要がある場合があります。

そして、あなたがする必要がない場合は殺さないでください!

for SIGNAL in TERM KILL; do
  for CHILD in $(jobs -s|sort -r); do
    kill -s $SIGNAL $CHILD
    sleep $MOMENT
  done
done

0

シェルスクリプトで子プロセスを強制終了:

多くの場合、何らかの理由でハングまたはブロックされている子プロセスを強制終了する必要があります。例えば。FTP接続の問題。

2つの方法があります。

1)タイムアウトに達したときに子プロセスを監視して強制終了する、子ごとに個別の新しい親を作成する。

次のようにtest.shを作成します。

#!/bin/bash

declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
for CMD in ${CMDs[*]}; do
    (sleep 10 & PID=$!; echo "Started $CMD => $PID"; sleep 5; echo "Killing $CMD => $PID"; kill $PID; echo "$CMD Completed.") &
done
exit;

次のコマンドを使用して、他の端末で「test」という名前のプロセスを監視します。

watch -n1 'ps x -o "%p %r %c" | grep "test" '

上記のスクリプトは、4つの新しい子プロセスとその親を作成します。各子プロセスは10秒間実行されます。しかし、5秒のタイムアウトに達すると、それぞれの親プロセスがそれらの子を殺します。そのため、子供は実行を完了することができません(10秒)。それらのタイミング(スイッチ10と5)を再生して、別の動作を確認します。その場合、子は10秒のタイムアウトに達する前に5秒で実行を終了します。

2)現在の親を監視させ、タイムアウトに達したら子プロセスを強制終了します。これは、各子を監視するための個別の親を作成しません。また、同じ親内のすべての子プロセスを適切に管理できます。

次のようにtest.shを作成します。

#!/bin/bash

declare -A CPIDs;
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")

CMD_TIME=15;
for CMD in ${CMDs[*]}; do
    (echo "Started..$CMD"; sleep $CMD_TIME; echo "$CMD Done";) &
    CPIDs[$!]="$RN";
    sleep 1;
done

GPID=$(ps -o pgid= $$);
CNT_TIME_OUT=10;
CNT=0;
while (true); do
    declare -A TMP_CPIDs;

    for PID in "${!CPIDs[@]}"; do
        echo "Checking "${CPIDs[$PID]}"=>"$PID;

        if ps -p $PID > /dev/null ; then
          echo "-->"${CPIDs[$PID]}"=>"$PID" is running..";
          TMP_CPIDs[$PID]=${CPIDs[$PID]};
        else
          echo "-->"${CPIDs[$PID]}"=>"$PID" is completed.";
        fi
    done

    if [ ${#TMP_CPIDs[@]} == 0 ]; then
        echo "All commands completed.";
        break;
    else
        unset CPIDs;
        declare -A CPIDs;
        for PID in "${!TMP_CPIDs[@]}"; do
            CPIDs[$PID]=${TMP_CPIDs[$PID]};
        done
        unset TMP_CPIDs;

        if [ $CNT -gt $CNT_TIME_OUT ]; then
            echo ${CPIDs[@]}"PIDs not reponding. Timeout reached $CNT sec. killing all childern with GPID $GPID..";
            kill -- -$GPID;
        fi
    fi

    CNT=$((CNT+1));
    echo "waiting since $b secs..";
    sleep 1;
done

exit;

次のコマンドを使用して、他の端末で「test」という名前のプロセスを監視します。

watch -n1 'ps x -o "%p %r %c" | grep "test" '

上記のスクリプトは4つの新しい子プロセスを作成します。すべての子プロセスのPIDを保存し、それらをループして、実行が終了しているか、実行中であるかを確認しています。子プロセスはCMD_TIME時間まで実行されます。ただし、CNT_TIME_OUTタイムアウトに達すると、すべての子が親プロセスによって強制終了されます。タイミングを切り替えてスクリプトで遊んで、動作を確認できます。このアプローチの欠点の1つは、すべての子ツリーを削除するためにグループIDを使用していることです。ただし、親プロセス自体は同じグループに属しているため、強制終了されます。

親をkillしたくない場合は、親プロセスに他のグループIDを割り当てる必要がある場合があります。

詳細については、こちらをご覧ください。

シェルスクリプトで子プロセスを強制終了する


0

このスクリプトも機能します:

#/bin/sh while true do echo "Enter parent process id [type quit for exit]" read ppid if [ $ppid -eq "quit" -o $ppid -eq "QUIT" ];then exit 0 fi for i in `ps -ef| awk '$3 == '$ppid' { print $2 }'` do echo killing $i kill $i done done


0

私はそれが古いことを知っていますが、それが私が見つけたより良い解決策です:

killtree() { 
    for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
        echo Terminating: $p 
        kill $p
    done
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.