向进程组的所有成员发送信号的最佳方法是什么?

2024-09-30 15:23:00
admin
原创
123
摘要:问题描述:我想终止整个进程树。使用任何常见的脚本语言执行此操作的最佳方法是什么?我正在寻找一个简单的解决方案。解决方案 1:您没有说明要终止的树是否是单个进程组。(如果树是从服务器启动或 shell 命令行分叉的结果,则通常就是这种情况。)您可以使用 GNU ps 发现进程组,如下所示: ps x -o &...

问题描述:

我想终止整个进程树。使用任何常见的脚本语言执行此操作的最佳方法是什么?我正在寻找一个简单的解决方案。


解决方案 1:

您没有说明要终止的树是否是单个进程组。(如果树是从服务器启动或 shell 命令行分叉的结果,则通常就是这种情况。)您可以使用 GNU ps 发现进程组,如下所示:

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

如果您要终止的是进程组,只需使用kill(1)命令,但不要为其指定进程号,而是为其指定组号的负数。例如,要终止组 5112 中的每个进程,请使用kill -TERM -- -5112

解决方案 2:

使用进程组 ID ( )终止属于同一进程树的所有进程PGID

  • kill -- -$PGID     使用默认信号 ( TERM= 15)

  • kill -9 -$PGID     使用信号KILL(9)

您可以从同一进程树PGID中的任何Process-ID ( ) 中检索PID

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

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

特别感谢tanagerSpeakus$PID剩余空间和 OSX 兼容性的贡献。

解释

  • kill -9 -"$PGID"=>KILL向所有子代和孙代发送信号 9()...

  • PGID=$(ps opgid= "$PID")=>从树中的任何Process-ID检索Process-Group-ID ,而不仅仅是Process-Parent-ID。 的变体是,其中可以被 替换。但是:

ps opgid= $PIDps -o pgid --no-headers $PIDpgidpgrp

+ **`ps`**`PID`当小于五位数字时插入前导空格,并右对齐,正如[tanager](https://stackoverflow.com/users/1022309)所注意到的。您可以使用:  
 **`PGID=$(ps opgid= "$PID" | tr -d ' ')`**
+ **`ps`**从 OSX 总是打印标题,因此[Speakus](https://stackoverflow.com/users/751932)建议:  
 **`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

结论

我注意到此示例中的PIDPGID相等 ( 28957)。

这就是我最初认为kill -- -$PID足够的原因。但在进程在 中生成的情况下,Makefile进程ID与组 ID不同。

我认为当从不同的组 ID(另一个进程树)kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)调用时,杀死整个进程树的最好的简单技巧。

解决方案 3:

pkill -TERM -P 27888

这将终止所有父进程 ID 为 27888 的进程。

或者更健壮:

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

该计划将在 33 秒后终止进程并礼貌地要求进程终止。

请参阅此答案以了解如何终止所有后代。

解决方案 4:

要递归地终止进程树,请使用 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 $@

解决方案 5:

pslist包中的rkill命令将给定的信号(或默认情况下)发送给指定进程及其所有后代:SIGTERM

rkill [-SIG] pid/name...

解决方案 6:

我使用了这里描述的方法的稍微修改版本:
https ://stackoverflow.com/a/5311362/563175

看起来是这样的:

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

其中 24901 是父级的 PID。

它看上去很丑,但是却能完美地完成它的工作。

解决方案 7:

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

解决方案 8:

我无法发表评论(声誉不够),所以我被迫添加一个新的答案,即使这不是真正的答案。

@olibre 在 2 月 28 日给出的答案非常全面,但有一个小问题。ps opgid= $PID对于少于五位数字的 PID,输出将包含前导空格,因为ps是对齐列(右对齐数字)。在整个命令行中,这会导致负号,后跟空格,后跟组 PID。简单的解决方案是通过管道ps删除tr空格:

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

解决方案 9:

awk布拉德的回答也是我推荐的,只不过如果您使用--ppid选项,您可以完全取消它ps

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

解决方案 10:

如果你知道传递父进程的 pid,那么这里有一个可以工作的 shell 脚本:

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

解决方案 11:

除了 Norman Ramsey 的回答之外,如果您想创建一个进程组,可能值得查看 setsid。http

://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html

如果调用进程不是进程组负责人,setsid() 函数将创建一个新会话。返回后,调用进程将成为此新会话的会话负责人,将成为新进程组的进程组负责人,并且没有控制终端。调用进程的进程组 ID 应设置为等于调用进程的进程 ID。调用进程将是新进程组中的唯一进程,也是新会话中的唯一进程。

我认为这意味着您可以从启动进程创建一个组。我在 php 中使用了此功能,以便能够在启动后终止整个进程树。

这可能是个坏主意。我很想听听大家的评论。

解决方案 12:

受到ysth 评论的启发

kill -- -PGID

不要给它一个进程号,而要给它组号的反码。与几乎所有命令一样,如果您希望以 开头的普通参数-不被解释为开关,请在其前面加上--

解决方案 13:

使用psutil在 python 中执行此操作非常容易。只需使用 pip 安装 psutil,然后您就会拥有一整套流程操作工具:

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

解决方案 14:

根据志刚的回答,这避免了自杀:

init_killtree() {
    local pid=$1 child

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

解决方案 15:

下面的 shell 函数与许多其他答案类似,但它可以在 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
}

解决方案 16:

如果你想通过名称终止一个进程:

killall -9 -g someprocessname

或者

pgrep someprocessname | xargs pkill -9 -g

解决方案 17:

这是我使用 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//,/ }

解决方案 18:

这是@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 上都运行良好。在无法依赖进程组管理能力的情况下(例如,编写脚本来测试必须在多个环境中构建的软件时),这种树遍历技术绝对有用。

解决方案 19:

谢谢大家的智慧。我的脚本在退出时会遗留一些子进程,否定提示让事情变得简单。我编写了这个函数,以便在必要时在其他脚本中使用:

# 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
}

干杯。

解决方案 20:

如果你的系统上有 pstree 和 perl,你可以尝试这个:

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

解决方案 21:

最好先杀死父级再杀死子级;否则父级可能会在自己被杀死之前再次产生新的子级。这些子级将在被杀死后幸存下来。

我的 ps 版本与上面的不同;可能太旧了,因此出现奇怪的 grepping......

使用 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

解决方案 22:

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

解决方案 23:

以下内容已在 FreeBSD、Linux 和 MacOS X 上进行了测试,仅依赖于 pgrep 和 kill(ps -o 版本在 BSD 下不起作用)。第一个参数是必须终止其子进程的父进程 pid。第二个参数是一个布尔值,用于确定是否也必须终止父进程 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

这将向 shell 脚本中的任何子进程/孙进程发送 SIGTERM,如果 SIGTERM 没有成功,它将等待 10 秒然后发送 kill。


先前的答案:

下面的方法也有效但是会在 BSD 上终止 shell 本身。

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

解决方案 24:

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

此版本将避免终止其祖先 - 这会导致以前的解决方案中出现大量子进程。

在确定子列表之前,进程会被正确停止,这样就不会有新的子进程被创建或消失。

被终止的作业必须继续运行才能从系统中消失。

解决方案 25:

我知道这是一个老问题,但是所有的回应似乎都在调用 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)}'

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

如果你不想杀死顶层进程,那么

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

就在 system() 行将其从终止集删除之前。

请记住,这里存在竞争条件,但就我所知,所有解决方案都是如此。它满足了我的需要,因为我需要它的脚本不会创建大量短暂的子进程。

读者可以练习一下将其变成 2 遍循环:第一遍之后,向 p 列表中的所有进程发送 SIGSTOP,然后循环再次运行 ps,第二遍之后发送 SIGTERM,然后发送 SIGCONT。如果你不关心好结局,那么第二遍可能只是 SIGKILL,我想。

解决方案 26:

要立即杀死整个进程组,就像这样^C做:

PID="$(pgrep -f unique_command_line_part)"
if [[ -n "$PID" ]]
then
    PGID="$(ps --no-headers -p $PID -o pgid)"
    kill -SIGINT -- -${PGID// /}
fi

此答案中解释了每一行

解决方案 27:

如果您知道要终止的进程的 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 )))

解决方案 28:

在 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

解决方案 29:

在 shell 脚本中终止子进程:

很多时候我们需要终止由于某种原因被挂起或阻塞的子进程。例如 FTP 连接问题。

有两种方法,

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 超时,所有子进程都将被父进程杀死。您可以切换时间并尝试使用脚本来查看行为。这种方法的一个缺点是,它使用组 ID 来杀死所有子树。但父进程本身属于同一组,因此它也将被杀死。

如果您不想父进程被终止,则可能需要为父进程分配其他组 ID。

更多详情请见此处,

在 shell 脚本中终止子进程

解决方案 30:

该脚本也有效:

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

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用