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