Why does a while loop stop after being suspended?

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.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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.

Method 2

I wrote one of the co-authors of Bash about the issue, and here is his reply:

It’s not really a bug, but it is certainly a caveat.

The idea here is that you suspend processes, which are a different
unit of granularity than shell commands. When a process is suspended,
it returns to the shell (with a non-zero status, which has
consequences when you, say, stop a process that’s the loop test),
which has a choice: it can break out of or continue the loop, leaving
the stopped process behind. Bash chooses — and has always chosen —
to break out of loops when a job is stopped. Continuing the loop is
rarely what you want.

Some other shells do things like fork a copy of the shell when a
process gets suspended due to SIGTSTP, and stop that process. Bash
hasn’t ever done that — it seems more complicated than the benefit
warrants — but if someone wants to submit that code as a patch, I’d
take a look at incorporating the changes.

So if anyone wants to submit a patch, use the email addresses found in the man pages.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x