如何在不杀死父级的情况下杀死所有子进程

时间:2014-10-24 01:18:12

标签: linux bash unix

我有一个脚本在开始时运行后台进程,然后到最后需要停止后台进程及其子进程,然后执行其他任务等,然后在必要时返回错误代码。我该怎么做呢?我已经看到了几个如何杀死整个树的例子,包括父进程(例如kill 0),但我希望主脚本继续运行并返回正确的错误代码。 E.g:

./some_process_with_child_processes &
./do_stuff
kill $! # doesnt kill child processes!
./do_other_stuff
exit 5

2 个答案:

答案 0 :(得分:0)

您需要在

之后立即捕获$!(子PID)
/some_process_with_child_processes &
pid=$!
./do_stuff
kill $pid
./do_other_stuff

答案 1 :(得分:0)

扩展Ed Heal的评论:

在父进程中,陷阱SIGTERM,例如

trap "echo received sigterm" SIGTERM

然后,当您想终止所有子进程时,在主脚本中执行

kill -s SIGTERM 0

父母也将收到SIGTERM,但这并不重要,因为它已被困信号。

您可能还发现在子进程中为EXIT设置陷阱很有用,这样他们就可以在收到SIGTERM时进行清理。

可以发送子进程SIGKILL,但这有点残酷,并且子进程没有机会进行任何清理。


修改

以下是一些脚本,用于说明父脚本如何单独杀死其子级,而不是使用kill -s SIGTERM 0整体杀死子级。

<强> traptest

#!/bin/bash

# trap test
# Written by PM 2Ring 2014.10.23

myname=$(basename "$0")
child=sleeploop
delay=${1:-5}
loops=${2:-5}

sig=SIGTERM 
# sig=SIGKILL 
# sig=SIGINT 

# killed=False

signal_children()
{
    killed=True
    for ipid in "${pids[@]}"
    do 
        echo "Sending $sig to $ipid"
        kill -s $sig $ipid
    done
}

set_INT_trap()
{
    msg="echo -e \"\n$myname received ^C, sending $sig to children\""
    trap "$msg; signal_children" SIGINT
}

trap "echo \"bye from $myname\"" EXIT

pids=()
for i in A B C;do 
    echo "running $child $i $delay ..."
    ./$child $i $delay  &
    pid=$!
#     echo -e "$child$i PID = $pid\n"
    pids+=($pid)
done
# echo "all child PIDs: ${pids[@]}"

echo "$myname PID = $$"
echo; ps; echo

set_INT_trap $sig
trap "echo $myname Received SIGTERM; signal_children" SIGTERM

echo "$myname sleeping..."
sleep 18 &
wait $!
echo "$myname awake"

[[ $killed != True ]] && { echo "$myname sending $sig"; signal_children; }

echo "$myname finished"

<强> sleeploop

#!/bin/bash

# child script for traptest
# Written by PM 2Ring 2014.10.23

myname="$(basename "$0")$1"
delay=$2
loops=${3:-5}

set_trap()
{
    sig=$1
    trap "echo -e '\n$myname received $sig signal';exit 0" $sig
}

trap "echo \"bye from $myname\"" EXIT
set_trap SIGTERM
# set_trap SIGINT

#Select sleep mode
if false
then
    echo "$myname using foreground sleep"
    Sleep()
    {
        sleep $delay
    }
else
    echo "$myname using background sleep"
    Sleep()
    {
        sleep "$delay" &
        wait $!
    }
fi

#Time to snooze :)
for ((i=0; i<loops; i++));
do
    echo "$i: $myname sleeping for $delay"
    Sleep
done

echo "$myname terminated normally"

如果你想管道traptest的输出,这甚至可以工作,例如尝试

{ ./traptest; echo >&2 exitcode $?; } | cat -n