This may sound trivial, but I'm pretty sure this question hasn't been asked, or at least I can't find it.
I'm looking for a way to construct an infinite wait (not necessarily a loop) with shell scripting so that it waits forever and can be killed (or technically, to receive a SIGTERM
). The following are known possible constructs and arguments against them:
while true; do sleep 1; done
This almost gets it, but since sleep
is an external command, when I send a SIGTERM
to the running script, it has to wait for the sleep
to finish first and then process the signal. Change sleep 1
to something like sleep 10
and the lag will be obvious. Also the solution wakes up the CPU every 1 second, which is not ideal.while true; do read; done
This is perfect when stdin
is tty. read
is a shell builtin and SIGTERM
arrives at the script instantly. But, when stdin
is /dev/null
, the script eats up all the CPU by helplessly running read
forever on /dev/null
.Thus a shell builtin construct that waits forever is required. Skimming through man dash
I didn't find such one - the only blocking builtins are read
and wait
, and I don't have idea how I can construct an ideal one using wait
.
The answer should be applicable to POSIX shell (effectively dash
), or less preferably, Bash.
Additional notes.
The situation where the first example doesn't work perfectly is more complex than I thought. With the following shell script:
#!/bin/sh
echo $$
while true; do
sleep 100
done
if you kill it at another tty, it terminates immediately. The funny thing begins when you attempt to do trapping. With this script:
#!/bin/sh
at_term() {
echo 'Terminated.'
exit 0
}
trap at_term TERM
echo $$
while true; do
sleep 20
done
What happens is exactly described in example 1. This happens with bash, dash and zsh. And it's under this condition that I'm seeking a "perfect" infinite look construct.
you can use a named pipe for your read:
mkfifo /tmp/mypipe
#or mknode /tmp/mypipe p
if you later want to send different arbitrary "signals" to the pipe, the read can be use in combination with a case statement to take appropriate actions (even useful ones)
while read SIGNAL; do
case "$SIGNAL" in
*EXIT*)break;;
*)echo "signal $SIGNAL is unsupported" >/dev/stderr;;
esac
done < /tmp/mypipe