If I have a Bash script like:
function repeat {
while :; do
echo repeating; sleep 1
done
}
repeat &
echo running once
running once
is printed once but repeat
's fork lives forever, printing endlessly.
How should I prevent repeat
from continuing to run after the script which created it has exited?
I thought maybe explicitly instantiating a new bash -c
interpreter would force it to exit as its parent has disappeared, but I guess orphaned processes are adopted by init
or PID 1.
Testing this using another file:
# repeat.bash
while :; do echo repeating; sleep 1; done
# fork.bash
bash -c "./repeat.bash & echo an exiting command"
Running ./fork.bash
still causes repeat.bash
to continue to run in the background forever.
The simple and lazy solution is to add the line to fork.bash
:
pkill repeat.bash
But you had better not have another important process with that name, or it will also be obliterated.
-
I wonder, if there is a better or accepted way to handle background jobs in forked shells that should exit when the script (or process) that created them has exited?
-
If there is no better way than blindly
pkilling
all processes with the same name, how should a repeating job that runs alongside something like a webserver be handled to exit? I want to avoid acron
job because the script is in agit
repository, and the code should be self-contained without changing system files in/etc/
.
Best Answer
This kills the background process before the script exits:
How it works
trap '[ "$pid" ] && kill "$pid"' EXIT
This creates a trap. Whenever the script is about to exit, the commands in single-quotes will be run. That command checks to see if the shell variable
pid
has been assigned a non-empty value. If it has, then the process associated withpid
is killed.pid=$!
This saves the process id of the preceding background command (
repeat &
) in the shell variablepid
.Improvement
As Patrick points out in the comments, there is a chance that the script could be killed after the background process starts but before the
pid
variable is set. We can handle that case with this code: