How do I kill background processes / jobs when my shell script exits?
I am looking for a way to clean up the mess when my top-level script exits.
Especially if I want to use set -e
, I wish the background process would die when the script exits.
Solution 1:
To clean up some mess, trap
can be used. It can provide a list of stuff executed when a specific signal arrives:
trap "echo hello" SIGINT
but can also be used to execute something if the shell exits:
trap "killall background" EXIT
It's a builtin, so help trap
will give you information (works with bash). If you only want to kill background jobs, you can do
trap 'kill $(jobs -p)' EXIT
Watch out to use single '
, to prevent the shell from substituting the $()
immediately.
Solution 2:
This works for me (improved thanks to the commenters):
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
kill -- -$$
sends a SIGTERM to the whole process group, thus killing also descendants.Specifying signal
EXIT
is useful when usingset -e
(more details here).
Solution 3:
Update: https://stackoverflow.com/a/53714583/302079 improves this by adding exit status and a cleanup function.
trap "exit" INT TERM
trap "kill 0" EXIT
Why convert INT
and TERM
to exit? Because both should trigger the kill 0
without entering an infinite loop.
Why trigger kill 0
on EXIT
? Because normal script exits should trigger kill 0
, too.
Why kill 0
? Because nested subshells need to be killed as well. This will take down the whole process tree.
Solution 4:
trap 'kill $(jobs -p)' EXIT
I would make only minor changes to Johannes' answer and use jobs -pr to limit the kill to running processes and add a few more signals to the list:
trap 'kill $(jobs -pr)' SIGINT SIGTERM EXIT