我想杀死整个进程树。使用通用脚本语言实现这一目标的最佳方法是什么?我在寻找一个简单的解决办法。
当前回答
我使用了这里描述的方法的一个稍微修改的版本: https://stackoverflow.com/a/5311362/563175
它看起来是这样的:
kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "`
其中24901是父PID。
它看起来很丑,但它的工作很完美。
其他回答
使用进程组ID (PGID)杀死属于同一进程树的所有进程
使用默认信号(TERM = 15) 使用信号kill (9)
您可以从同一进程树的任何进程id (PID)中检索PGID
杀- - - $ (ps - o pgid = $ PID | grep - o '[0 - 9] * ')(信号) $(ps -o pgid= $PID | grep -o '[0-9]*')(信号kill)
特别感谢tanager和Speakus对$PID剩余空间和OSX兼容性的贡献。
解释
kill -9 -"$PGID" => Send signal 9 (KILL) to all child and grandchild... PGID=$(ps opgid= "$PID") => Retrieve the Process-Group-ID from any Process-ID of the tree, not only the Process-Parent-ID. A variation of ps opgid= $PID is ps -o pgid --no-headers $PID where pgid can be replaced by pgrp. But: ps inserts leading spaces when PID is less than five digits and right aligned as noticed by tanager. You can use: PGID=$(ps opgid= "$PID" | tr -d ' ') ps from OSX always print the header, therefore Speakus proposes: PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )" grep -o [0-9]* prints successive digits only (does not print spaces or alphabetical headers).
更多命令行
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在终止整个树的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)。 这就是为什么我一开始觉得杀了。-$PID就够了。但如果进程是在Makefile中生成的,则进程ID与组ID不同。
我认为kill——-$(ps -o pgid= $PID | grep -o[0-9]*)是当从不同的Group ID(另一个进程树)调用时杀死整个进程树的最好的简单技巧。
如果你知道传递父进程的pid,下面这个shell脚本应该可以工作:
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
要递归地杀死进程树,请使用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 $@
我进一步开发了志刚、木uri和固颈的溶液:
#!/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
这个版本将避免杀死它的祖先进程——在以前的解决方案中,这会导致大量的子进程。
在确定子列表之前适当地停止进程,这样就不会创建或消失新的子列表。
停止的作业被杀死后,必须继续执行,才能从系统中消失。
在杀死孩子之前杀死父母可能更好;否则,父母可能会在他自杀之前再次繁殖新的孩子。这些会在杀戮中幸存下来。
我的ps版本和上面的不一样;也许太老了,所以奇怪的握手……
使用shell脚本而不是shell函数有很多优点…
然而,这基本上是志刚的想法
#!/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