我想杀死整个进程树。使用通用脚本语言实现这一目标的最佳方法是什么?我在寻找一个简单的解决办法。


当前回答

在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

其他回答

使用进程组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(另一个进程树)调用时杀死整个进程树的最好的简单技巧。

你没有说你想要杀死的树是否是一个单独的进程组。(如果树是从服务器启动或shell命令行派生出来的,通常会出现这种情况。)您可以使用GNU ps发现进程组,如下所示:

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

如果它是您想要终止的进程组,只需使用kill(1)命令,但不给它一个进程号,而是给它一个组号的负数。例如,要终止5112组中的所有进程,请使用kill -TERM——-5112。

这个脚本也可以工作:

# / bin / sh 而真正的 做 输入父进程id [type quit for exit] 读ppid if [$ppid -eq "quit" -o $ppid -eq "quit"] 退出0 fi I in ' ps -ef| awk '$3 == '$ppid' {print $2}' ' 做 Echo杀死$i 杀死我美元 完成 完成

在杀死孩子之前杀死父母可能更好;否则,父母可能会在他自杀之前再次繁殖新的孩子。这些会在杀戮中幸存下来。

我的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

老问题,我知道,但所有的回答似乎都叫ps,我不喜欢。

这个基于awc的解决方案不需要递归,只调用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)}'

基本思想是,我们建立一个父:子条目的数组(a),然后循环数组为匹配的父元素查找子元素,并将它们添加到我们的父元素列表(p)中。

如果您不想终止顶级进程,则执行

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

就在system()行将它从kill集中移除之前。

请记住,这里存在一个竞争条件,但这对所有的解都是正确的(据我所知)。它做了我所需要的,因为我需要它的脚本不会创建大量短暂的子进程。

对于读者来说,一个练习是将其设置为2次循环:在第一次传递之后,将SIGSTOP发送给p列表中的所有进程,然后再次循环运行ps,在第二次传递之后发送SIGTERM,然后是SIGCONT。如果你不关心美好的结局,那么第二遍可能只是SIGKILL,我想。