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


当前回答

如果你知道你想要杀死的东西的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 )))

其他回答

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

它看起来是这样的:

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

其中24901是父PID。

它看起来很丑,但它的工作很完美。

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

rkill [-SIG] pid/name...

我不能评论(没有足够的声誉),所以我被迫添加一个新的答案,即使这不是一个真正的答案。

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

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

如果你知道传递父进程的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

现在是一些聪明的shell编程。

这种解决方案是有代价的,但至少它是基于日常迭代和递归的。通过仔细注意排版命令并在适当的地方将它们转换为声明或本地,可以将其转换为bash。

讨论

当终止一个进程时,必须面对这样的现实:它可能是许多子进程的父进程,而每个子进程又可能是更多子进程的父进程,等等等等。

怎么办呢?

如果有一个函数来测试进程是否有子进程,以及另一个函数来返回父进程的子pid就好了。

这样,游戏就简单多了,因为你可以创建一个循环来遍历pid列表,在杀死它之前检查每个pid是否有子代。如果没有子进程,则直接终止该进程。如果有子函数,递归地调用驱动函数,并将获得父函数子函数pid的函数的结果传递给它。

基本案例操作(流程没有子过程)。

#!/bin/ksh

function killProcess ()
{
    typeset -r PID=$1

    if [[ ! isProcess $PID ]]
    then
        echo -e "Process $PID cannot be terminated because it does not exist.\n" 1>&2
        return 1
    elif [[ kill -s TERM $PID ]] && [[ ! isProcess $PID ]]
    then
        echo -e "Process $PID was terminated.\n" 1>&2
        return 0
    elif kill -s KILL $PID
        echo -e "Process $PID killed with SIGKILL (9) signal. No time to clean up potential files.\n" 1>&2
        return 0
    elif isZombie $PID
    then
        echo -e "Process $PID in the zombie status.\n" 1>&2 
        return 2
    else
        echo -e "Process $PID is alive. SIGTERM and SIGKILL had no effect. It is not a zombie.\n" 1>&2
    fi

    return 3
}

function attemptToKillPid ()
{
    typeset -r PID=$1

    if killProcess $PID
    then 
        return 0
    fi

    ppid=$(getParentPid $pid)
    echo -e "Process $pid of parent $ppid was not able to be killed.\n" 1>&2
    return 1
}

一般案例操作(流程有子)。

function killPidFamily ()
{
    typeset -r PROCESSES=$*
    typeset -ir NUM_PROCESSES_TO_KILL=$(countLines $PROCESSES)
    typeset -i numKilledProcesses=0
    typeset ppid

    for pid in $PROCESSES
    do
        pid=$(trim $pid)

        if ! hasChildPids $pid
        then
            attemptToKillPid $pid && (( numKilledProcesses++ ))
        else
            killPidFamily $(getChildPids $pid) && attemptToKillPid $pid && (( numKilledProcesses++ ))
        fi
    done

    (( numKilledProcesses == NUM_PROCESSES_TO_KILL ))
    return $?
}

支持函数库。

#!/bin/ksh

function trim ()
{
    echo -n "$1" | tr -d [:space:]
}

function countLines ()
{
    typeset -r $LIST=$*
    trim $(echo $LIST | wc -l | awk {'print $1'})
}

function getProcesses ()
{
    # NOTE: -o pgid below would be $4 in awk.

    ps -e -o comm,pid,ppid,pgid,user,ruid,euid,group,rgid,egid,etime,etimes,stat --no-headers
}

function getProcess ()
{
   typeset -r PID=$1
   ps -p $PID -o comm,pid,ppid,pgid,user,ruid,euid,group,rgid,egid,etime,etimes,stat --no-headers
}

function isProcess ()
{
    typeset -r PID=$1

    ps -p $PID -o pid --no-headers 1>&2
    return $?
}

function getProcessStatus ()
{
    typeset -r PID=$1
    trim $(ps -p $PID -o stat --no-headers)
}

function isZombie ()
{
    typeset -r PID=$1
    typeset processStatus

    processStatus=$(getProcessStatus $PID)

    [[ "$processStatus" == "Z" ]]
    return $?
}

function hasChildPids ()
{
    typeset -r PPID=$1
    echo $(getProcesses) | awk '{print $3}' | sort -n | uniq | grep "^${PPID}$"
    return $?
}

function getChildPids ()
{
    typeset -r PPID=$1
    echo $(getProcesses) | awk '{print $2, $3}' | sort -k 2 | awk "\$2 == $PPID {print \$1}" | sort -n
}

function getParentPid ()
{
    typeset -r PID=$1
    trim $(echo $(getProcess $PID) | awk '{print $3}')
}

通过这种方式,您可以确定流程树正在从分支被销毁,并向上移动到根。这有助于避免潜在的僵尸和其他不受欢迎的情况。

现在,您已经看到了执行此操作的最昂贵的方法(一次杀死一个进程),研究如何更改此解决方案以使用PGID(进程组ID)。getProcesses()函数已经打印了PGID(在awk中为4美元),所以学习如何使用它,或者不要使用它。