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.
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.
This works for me (improved thanks to the commenters):
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
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.
function cleanup_func {
sleep 0.5
echo cleanup
}
trap "exit \$exit_code" INT TERM
trap "exit_code=\$?; cleanup_func; kill 0" EXIT
# exit 1
# exit 0
Like https://stackoverflow.com/a/22644006/10082476, but with added exit-code
I finally have found a solution that appears to work in all cases to kill all descents recursively regardless of whether they are jobs, or sub-processes. The other solutions here all seemed to fail with things such as:
while ! ffmpeg ....
do
sleep 1
done
In my situation, ffmpeg would keep running after the parent script exited.
I found a solution here to recursively getting the PIDs of all child processes recursively and used that in the trap handler thus:
cleanup() {
# kill all processes whose parent is this process
kill $(pidtree $$ | tac)
}
pidtree() (
[ -n "$ZSH_VERSION" ] && setopt shwordsplit
declare -A CHILDS
while read P PP;do
CHILDS[$PP]+=" $P"
done < <(ps -e -o pid= -o ppid=)
walk() {
echo $1
for i in ${CHILDS[$1]};do
walk $i
done
}
for i in "$@";do
walk $i
done
)
trap cleanup EXIT
The above placed at the start of a bash script succeeds in killing all child processes. Note that pidtree is called with $$ which is the PID of the bash script that is exiting and the list of PIDs (one per line) is reversed using tac to try and ensure that prarent processes are killed only after their children to avoid possible race conditions in loops such as the example I gave.
So script the loading of the script. Run a killall (or whatever is available on your OS) command that executes as soon as the script is finished.
Another option is it to have the script set itself as the process group leader, and trap a killpg on your process group on exit.
Just for diversity I will post variation of https://stackoverflow.com/a/2173421/102484 , because that solution leads to message "Terminated" in my environment:
trap 'test -z "$intrap" && export intrap=1 && kill -- -$$' SIGINT SIGTERM EXIT