MaxB MaxB - 4 months ago 10
Linux Question

Can't kill a Bash command that sleeps in a loop

I tried to run

$ (while true; do cat some_small_file ; sleep 1; done) | some_script


to test
some_script
, but
some_script
wasn't in my
$PATH
, so that printed

some_script command not found


The weird things is that
Bash
did not return, and
Ctrl-C
does nothing.

I even tried killing
sleep
with

ps -u maxb | grep sleep | cut -f1 -d " " | xargs kill -9


in another terminal, but that, of course just continues to the next loop iteration.

Is it possible to make
Bash
terminate that command?

Answer

There are a few approaches you can take here.

One is to move both cat and sleep into the conditional part of the while syntax, such that the loop will fail as soon as either of those commands does not succeed:

{ while cat some_small_file && sleep 1; do :; done; } | some_script

A similar option is to put an explicit break into the loop, conditioned (again) on such failure:

{ while :; do cat some_small_file && sleep 1 || break; done; } | some_script

Note that : is an exact synonym for true. (In every shell I've seen -- not just bash but even Busybox ash -- they're literally implemented by the same internal function).


Also note that the most important part of this solution is exiting the loop if cat fails, not if sleep fails: Exiting on a sleep failure will address the kill problem, but exiting on a cat failure will handle the (much more likely) case that some_script has exited and thus writing to it is resulting in a SIGPIPE.


By the way -- the change from ( ) to { ...; } is to potentially reduce the number of transient subshells: While pipelines always create implicit subshells, there's no particular reason to use syntax that may (in some cases -- there are pertinent details which are implementation- rather than standard-defined) create an explicit subshell in addition to the mandatory implicit one(s).

It's thus a very minor efficiency improvement, but not essential to this answer: You'd get the same behavior if you stuck with the original parenthesis but modified the flow control constructs in the same way.