Bash – Why Does a While Loop Stop After Being Suspended?

background-processbashshellsignals

Why is it that using bash and suspending a while loop, the loop stops after being resumed? Short example below.

$ while true; do echo .; sleep 1; done
.
.
^Z
[1]+  Stopped                 sleep 1
$ fg
sleep 1
$

I'm familiar with signals, and I'm guessing this may be the natural behaviour of bash here, but I'd like to better understand why it behaves in this particular way.

Best Answer

This looks like a bug in several shells, it works as expected with ksh93 and zsh.

Background:

Most shells seem to run the while loop inside the main shell and

Bourne Shell suspends the whole shell if you type ^Z with a non-login shell

bash suspends only the sleep and then leaves the while loop in favor of printing a new shell prompt

dash makes this command unsuspendable

With ksh93, things work very different:

ksh93 does the same, while the command is started the first time, but as sleep is a buitin in ksh93, ksh93 has a handler that causes the while loop to fork off the main shell and then suspend at the time when you type ^Z.

If you in ksh93 later type fg, the forked off child that still runs the loop is continued.

You see the main difference when comparing the jobcontrol messages from bash and ksh93:

bash reports:

[1]+ Stopped sleep 1

but ksh93 reports:

^Z[1] + Stopped while true; do echo .; sleep 1; done

zsh behaves similar to ksh93

With both shells, you have a single process (the main shell) as long as you don't type ^Z, and two shell processes after you typed ^Z.

Related Question