web-dev-qa-db-ja.com

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

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

404
Adam Peck

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

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

それがあなたが殺したいプロセスグループであるならば、単にそれにプロセス番号を与える代わりにkill(1)コマンドを使ってください、それにグループの否定数。たとえば、グループ5112のすべてのプロセスを強制終了するには、kill -TERM -- -5112を使用します。

286
Norman Ramsey

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

  • kill -- -$PGIDデフォルトのシグナルを使用(TERM = 15)
  • kill -9 -$PGID信号KILL9)を使用

同じプロセスツリーの任意のProcess-IDPGID)からPIDを取得できます。

  • _(kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*')(シグナルTERM
  • kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*')(シグナルKILL

tanager および Speakus に、$PIDの残りスペースとOSX互換性への貢献に感謝します。

説明

  • kill -9 -"$PGID"=>シグナル9(KILL)をすべての子および孫に送信しています...
  • PGID=$(ps opgid= "$PID")=> Process-Parent-IDだけでなく、ツリーのProcess-IDからProcess-Group-IDを取得します。ps opgid= $PIDのバリエーションps -o pgid --no-headers $PIDです。ここで、pgidpgrpに置き換えることができます。
    しかし:
    • psは、PIDが5桁より少なく、 tanager で示されるように右寄せされている場合に先行スペースを挿入します。
      PGID=$(ps opgid= "$PID" | tr -d ' ')
    • OSXからのpsは常にヘッダーを表示するので、 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)

制限

  • davide および Hubert 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

結論

この例では、PIDPGIDが等しい(28957)ことに気付きました。
これが私がもともとkill -- -$PIDで十分だと思った理由です。しかし、プロセスがMakefile内に生成される場合、Process IDGroup IDと異なります。

私はkill -- -$(ps -o pgid= $PID | grep -o [0-9]*)が異なるGroup ID(別のプロセスツリー)から呼ばれたときにプロセスツリー全体を殺すための最も簡単なトリックだと思います。

188
olibre
pkill -TERM -P 27888

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

またはもっと堅牢:

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

これは33秒後に殺害を予定し、プロセスを終了するように丁寧に依頼します。

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

164
Onlyjob

プロセスツリーを再帰的にkillするには、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 $@
100
zhigang

rkillコマンドpslistパッケージは指定されたシグナル(またはデフォルトではSIGTERM)を指定されたプロセスとそのすべての子孫に送ります:

rkill [-SIG] pid/name...
16
Onlyjob

awk--ppidオプションを使用すれば、psを完全に廃止できることを除けば、bradの答えは私もお勧めします。

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

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
9
x-yuri

ここで説明している方法を少し修正したものを使用します。 https://stackoverflow.com/a/5311362/563175

だからそれはそのようになります:

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

24901は親のPIDです。

それはかなり醜い見えますが、それは完全に仕事をしていますか。

9
Tomasz Werszko

あなたが親プロセスの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
9
brad.lane

Norman Ramseyの答えに追加するために、プロセスグループを作成したいのならsetsidを見る価値があるかもしれません。
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html

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

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

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

8
hajamie

私はコメントすることができない(評判が十分ではない)ので、これは本当に答えではないが、私は新しい答えを追加することを余儀なくされている。

psは列を正当化するため、ps opgid= $PIDの出力には5桁より短いPIDの先行スペースが含まれます(厳密に数字を揃えます)。コマンドライン全体では、これは負の符号、スペース、グループPIDの順になります。簡単な解決策は、スペースを削除するためにpstrにパイプ処理することです。

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

からのコメント

kill -- -PGID

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

7
Steven Penny

次のシェル関数は他の多くの答えに似ていますが、pgrepのような外部の依存関係なしにLinuxとBSD(OS Xなど)の両方で機能します。

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

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
genericdave

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

killall -9 -g someprocessname

または

pgrep someprocessname | xargs pkill -9 -g
4
Mika Vatanen

Zhigangの答えに基づいて、これは自己殺害を避けます。

init_killtree() {
    local pid=$1 child

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

これは@ 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でも問題なく動作するようです。複数の環境で構築しなければならないソフトウェアをテストするためのスクリプトを書くときのように、プロセスグループを管理できないことに頼ることができない状況では、このツリーウォーキング手法は間違いなく役に立ちます。

3
solidsnack

これは、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//,/ }
3
marekdef

昔の質問ですが、私は知っていますが、すべての応答がpsを呼び出し続けているように思われますが、これは嫌いでした。

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

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()行がkillセットから削除される直前に。

ここに競合状態があることを心に留めておいてください、しかしそれは(私が見ることができる限りでは)すべての解決策の本当です。私が必要としていたスクリプトは短命の子供をたくさん作成しないので、それは私が必要としていることを行います。

2回目のループにすることです。最初のパスの後にpリスト内のすべてのプロセスにSIGSTOPを送信し、次にpsを実行するためのループを繰り返し、2番目のパスの後にSIGTERMを送信してからSIGCONTを送信します。もしあなたがニースエンディングを気にしないのであれば、セカンドパスは単にSIGKILLになるでしょう、と私は思います。

1
Whinger

皆さんの知恵をありがとう。私のスクリプトは終了時にいくつかの子プロセスを残していました、そして否定チップは物事をより簡単にしました。必要に応じて他のスクリプトでも使えるようにこの関数を書きました。

# 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
Donald Duck

Zhigang、xyuri、solidneckのソリューションをさらに開発します。

 #!/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
Peter Steier

子供の前に親を殺すのがおそらくより良いです。そうでなければ、親は自分が殺される前に再び新しい子供を産む可能性があります。これらは殺害を乗り切るでしょう。

私のpsのバージョンは上記のものとは異なります。古すぎるかもしれない、それ故に奇妙なgrepping ...

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

しかし、それは基本的にzhigangsの考えです


#!/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
1
Peter Steier

以下は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
Orsiris de Jong

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
jens

あなたがあなたが殺したいもののpidを知っているなら、あなたは通常セッションIDと同じセッション内のすべてから行くことができます。私は二重チェックをしたいのですが、rsyncを終了させたいループ内で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
Morgan

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

killtree() { 
    for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
        echo Terminating: $p 
        kill $p
    done
}

あなたのシステムにpstreeとPerlがあるなら、これを試すことができます。

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

このスクリプトも動作します:

#/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
Nullpointer

グループに属するプロセスの名前のみを使用してグループを強制終了する:

kill -- -$(ps -ae o pid,pgrp,cmd | grep "[e]xample.py" | awk '{print $2}' | tail -1)

これはolibreの答えを修正したものですが、PIDを知る必要はなく、単にグループのメンバーの名前です。

説明:

グループIDを取得するには、表示されている引数を使用してpsコマンドを実行し、それをコマンドにgrepします。グループIDである2番目のフィールドを取得するためにawk。末尾-1は重複したグループIDを取り除きます。あなたは$()シンタックスを使用してそれらすべてを変数に入れます - そして、必要に応じて - あなたはグループIDを取得します。そのため、上記のそのgroupidをその$(mess)に置き換えます。

0
CoderBlue
ps -o pid= --ppid $PPID | xargs kill -9 
0
Harpreet

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

多くの場合、何らかの理由で絞首刑またはブロックされている子プロセスを強制終了する必要があります。例えば。 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を使用していることです。しかし、親プロセス自体も同じグループに属しているため、強制終了されます。

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

詳細はこちら

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

0
Hemant Thorat