Let's consider the case of a shell script that wants to keep an eye on a log file. The log file gets populated with useful information during the execution of the script itself.
tail -F /var/log/x.log &
generate_logs --output=/var/log/x.log
This obviously works, but the shell termination won't kill the tail
process, so the script execution will leak resources.
As a solution, the POSIX shell provides a built-in named trap
, documented
here.
In short it allows to define actions to be executed upon signaling, and
that includes shell termination. It is similar to atexit(3)
in POSIX C.
The previous script becomes then something like this:
tail -F /var/log/x.log &
tailpid="$!"
atexit() {
kill -term "$tailpid"
}
trap atexit EXIT
generate_logs --output=/var/log/x.log
This will terminate the tail
process when the shell exits. Note that it
will not work when the shell is interrupted (e.g. SIGINT
), but it should
be enough to associate the trap with a couple of additional signals.
Oh, and I just hope that
tail
does not die before the shell terminates, because the shell won't forgettail
's pid. The shell will try to killtail
even if it is no longer alive. Will the same pid be taken by another process?
What I found out today is that if you are running GNU tail
, you can simply
rely on the --pid
option:
tail -F /var/log/x.log --pid="$$" &
generate_logs --output=/var/log/x.log
Or even (if that fits your needs):
generate_logs --output=/var/log/x.log &
tail -F /var/log/x.log --pid="$!"
GNU tail
will just keep an eye on the specified pid, and terminate
together with the corresponding process. This is very elegant, but
unfortunately not portable.
EDIT
I forgot to mention (and thanks kn for reminding me of it) that
kill
system call
accepts 0 as a pseudo process identifier, which allows to kill all processes
having the same process group. The
kill command
allows for the same semantics, so we have also this possibility:
tail -F /var/log/x.log &
trap 'kill -term 0' EXIT
generate_logs --output=/var/log/x.log
It is less specific, as every process having the same process group will be signaled, but it is also good enough in most cases, and way simpler :)
NOTES
Comments on lobsters.
Thanks to kn on Lobsters: he spotted a typo
(I wrote $?
instead of $!
in my introduction) and mentioned kill 0
.