How do you kill all child processes without killing the parent

前端 未结 2 1626
夕颜
夕颜 2021-01-15 06:25

I have a script which runs a background process at the beginning, then towards the end needs to stop that background process and its children, THEN do some other tasks etc t

相关标签:
2条回答
  • 2021-01-15 07:14

    To expand on Ed Heal's comment:

    In the parent process, trap SIGTERM, eg

    trap "echo received sigterm" SIGTERM

    Then, when you want to terminate all the child processes, in the main script do

    kill -s SIGTERM 0

    The parent will also receive SIGTERM, but that doesn't matter, since it's trapped that signal.

    You may also find it useful to set a trap for EXIT in the child processes so they can clean up when they receive SIGTERM.

    You could send the child processes SIGKILL, but that's a bit brutal, and the child processes won't have a chance to do any clean up.


    edit

    Here are some scripts that illustrate how a parent script can kill its children individually, rather than en masse using 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"
    

    This even works if you want to pipe the output of traptest, eg try

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

    0 讨论(0)
  • 2021-01-15 07:18

    You need to capture the value of $! (the child PID) right after

    /some_process_with_child_processes &
    pid=$!
    ./do_stuff
    kill $pid
    ./do_other_stuff
    
    0 讨论(0)
提交回复
热议问题