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


当前回答

在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)来玩游戏,看看另一种行为。在这种情况下,child将在达到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" '

Above script will create 4 new child processes. We are storing pids of all child process and looping over them to check if they are finished their execution or still running. Child process will execution till CMD_TIME time. But if CNT_TIME_OUT timeout reach , All children will get killed by parent process. You can switch timing and play around with script to see behavior. One drawback of this approach is , it is using group id for killing all child tree. But parent process itself belong to same group so it will also get killed.

如果不希望终止父进程,可能需要为父进程分配其他组id。

更多细节可以在这里找到,

在shell脚本中杀死子进程

其他回答

根据志刚的回答,这可以避免自我毁灭:

init_killtree() {
    local pid=$1 child

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

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

它看起来是这样的:

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

其中24901是父PID。

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

修改后的志刚回答:

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

这个脚本也可以工作:

# / 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 杀死我美元 完成 完成

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