Say I have a script like the following

#!/usr/bin/env zsh
./subscript.sh &
# do stuff
# do more stuff

I would like the process running subscript.sh (and any processes initiated by it) to be fully terminated whenever:

  1. The script above exits
  2. The script above is killed for any reason.

How can I enforce the above? Is this automatically taken care of by the shell?

  • For points 1 & 3, you just need to have the pid of the backgrounded process (in bash $!). For #2, you can use a signal handler (in bash, trap -- dunno ksh or I'd give a proper answer) for SIGTERM; note nothing can respond to SIGKILL. trap can also work with EXIT or ERR, so you could use it (presuming there's a ksh equivalent) for all three purposes (saving a lot of checking WRT #3. – goldilocks Jun 16 '14 at 18:53
  • Also a bit of this perhaps: stackoverflow.com/questions/20362361/… – Konstantinos Jun 16 '14 at 23:16

The shell will definitely not spontaneously kill its subprocesses — after all a background job is supposed to run in the background and not care about the life of its parent. (An interactive shell will in some circumstances kill its subprocesses when it exits — which is not always desirable, hence nohup.)

You can make the shell script kill its background jobs when it exits or is killed by a catchable signal. Record the process IDs of the jobs, and kill them from a trap. Note that this only kills the jobs (as in, the original process that's started in the background), not their subprocesses.

trap '((#jobs == 0)) || kill $jobs' EXIT HUP TERM INT
subscript1 & jobs+=($!)
subscript2 & jobs+=($!)

If you want to be sure to kill all processes and their subprocesses, more planning is in order. One method is to arrange for all the processes to have a unique file open. To kill them all, kill all the processes that have this file open. A subprocess can escape by closing the file.

exec 3<"$lock_file"
exec 3<&-
fuser -k "$lock_file"
exit $status
  • Note that only interactive shells create process groups by default. And the process group id of a job is not necessarily the same as the pid stored in $! (try for instance sleep 10 | sleep 20 & ps -j; echo $! – Stéphane Chazelas Jun 22 '14 at 19:36
  • @StéphaneChazelas In what circumstances does zsh not create a process group for background jobs? – Gilles 'SO- stop being evil' Jun 22 '14 at 21:02
  • In non-interactive shells (zsh -c, scripts...). Process groups are for terminal job control primarily (the term "job" is usually associated with those process groups started by interactive shells. For instance sleep 1 | sleep 2 & in an interactive shell starts a process group of pgid x with two processes y and z. kill % will kill that job (unless there's also a suspended job), kill -- -x as well (x would generally be the same as one of y or z more likely y in my tests with zsh, while $! will be z). – Stéphane Chazelas Jun 22 '14 at 21:25
  • @StéphaneChazelas Gah! I'm stupid… I tested, and I swapped columns in the ps output and came to the wrong conclusion that zsh was (unlike other shells) creating a process group. I should have straced or source-dived. – Gilles 'SO- stop being evil' Jun 22 '14 at 21:39

You could trap EXIT and send a signal to the process group of the script.

on Linux

trap 'kill -SIGTERM 0' EXIT

on FreeBSD

trap 'kill -15 -$$' EXIT

SIGKILL is the only signal where this wouldn't work since the script is killed unconditionally.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.