445

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

4

34 に答える 34

320

殺したいツリーが単一のプロセスグループであるかどうかはわかりません。(これは、ツリーがサーバーの起動またはシェルコマンドラインからのフォークの結果である場合によくあります。)次のように、GNUpsを使用してプロセスグループを検出できます。

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

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

于 2008-12-24T20:06:02.990 に答える
219

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

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

同じプロセス ツリーPGIDの任意のプロセス ID ( ) から取得できます。PID

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

残りのスペースと OSX の互換性について貢献してくれたtanagerSpeakusに感謝します。$PID

説明

  • kill -9 -"$PGID"KILL=>すべての子と孫にシグナル 9 ( ) を送信...
  • PGID=$(ps opgid= "$PID")=> Process-Parent-IDだけでなく、ツリーの任意のProcess- ID から Process-Group-IDを取得します。is whereのバリエーションは、で置き換えることができます。しかし: ps opgid= $PIDps -o pgid --no-headers $PIDpgidpgrp
    • psが 5 桁未満で、 tanagerPIDの通知に従って右揃えの場合、先頭にスペースを挿入します。以下を使用できます。
      PGID=$(ps opgid= "$PID" | tr -d ' ')
    • psfrom OSX は常にヘッダーを出力するため、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)

制限

  • davideHubert 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ただし、プロセスが Process ID 内で生成される場合、MakefileProcess IDはGroup IDとは異なります。

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

于 2013-02-28T15:46:32.620 に答える
170
pkill -TERM -P 27888

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

またはより堅牢:

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

これは、33 秒後に強制終了をスケジュールし、丁寧にプロセスを終了するように依頼します。

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

于 2011-06-26T00:16:08.267 に答える
105

プロセス ツリーを再帰的に強制終了するには、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 $@
于 2010-07-09T08:57:02.157 に答える
12

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

したがって、次のようになります。

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

ここで、24901 は親の PID です。

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

于 2011-12-17T12:59:46.470 に答える
11

ブラッドの答えは私もお勧めしますが、へのオプションをawk使用すれば完全に廃止することができます。--ppidps

for child in $(ps -o pid -ax --ppid $PPID) do ....... done
于 2008-12-24T20:00:54.607 に答える
10

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
于 2012-11-20T20:31:17.840 に答える
9

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

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

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

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

于 2011-06-28T16:31:13.713 に答える
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
于 2008-12-24T19:34:32.617 に答える
8

ysth のコメントに触発されて

kill -- -PGID

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

于 2012-12-31T02:44:34.420 に答える
6

zhigang の回答に基づいて、これは自滅を回避します。

init_killtree() {
    local pid=$1 child

    for child in $(pgrep -P $pid); do
        init_killtree $child
    done
    [ $pid -ne $$ ] && kill -kill $pid
}
于 2013-10-30T11:49:16.590 に答える
6

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

def killChildren(pid):
    parent = psutil.Process(pid)
    for child in parent.get_children(True):
        if child.is_running():
            child.terminate()
于 2013-08-19T12:54:28.513 に答える
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
}
于 2013-08-13T16:43:13.993 に答える
4

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

killall -9 -g someprocessname

また

pgrep someprocessname | xargs pkill -9 -g
于 2014-10-03T09:49:29.150 に答える
3

これは、AWKを使用せずに、Bashのネイティブ解析の可能性のみに依存する@zigangの回答のバリエーションです。

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 の両方で問題なく動作するようです。複数の環境で構築する必要があるソフトウェアの一部をテストするためのスクリプトを作成する場合など、プロセス グループを管理できることに頼ることができない状況では、このツリー ウォーク手法は間違いなく役に立ちます。

于 2014-02-27T07:06:35.427 に答える
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
}

乾杯。

于 2009-01-09T20:45:30.790 に答える
1

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

私のバージョンの ps は上記のものとは異なります。おそらく古すぎるので、奇妙なgrepping...

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

ただし、基本的にはジガングの考えです


#!/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
于 2011-02-04T22:32:12.710 に答える
1

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

perl -e 'kill 9, (`pstree -p PID` =~ m/\((\d+)\)/sg)'
于 2010-07-27T05:10:21.133 に答える
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
于 2015-09-14T13:48:50.763 に答える
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
于 2014-12-21T21:01:00.130 に答える
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 )))
于 2011-03-28T21:39:49.767 に答える
0
ps -o pid= --ppid $PPID | xargs kill -9 
于 2009-03-02T11:49:05.743 に答える