I'm relatively new to bash scripts and have a question about something that seams strange to me.
I have written this as part of one of my bash scripts:
sleep 10 &
while [[ $(jobs) ]]
do
sleep 1
echo test
jobs &> /dev/null
done
Why is the "jobs" line necessary? If I leave it out it will run forever. but if I run:
sleep 10 & while [[ $(jobs) ]]; do sleep 1; echo test; done
directly from a bash It works. I have tried this on multiple systems.
Fun observation.
This happens because one feature of jobs
is to show a status when a job has terminated, but only if you haven't already been notified:
$ true & sleep 1; jobs;
[1] 18687
[1]+ Done true
It would be annoying and unhelpful if you saw "[1]+ Done" every single time you ever ran jobs
again in that shell. Therefore, jobs
will also clean up the job table so that a second jobs
won't notify you again:
$ true & sleep 1; jobs; echo "Here's the output the second time:"; jobs
[1] 18694
[1]+ Done true
Here's the output the second time:
(nothing)
Now, when you run $(jobs)
, you create a subshell. Due to how the Unix programming model works, $(jobs)
is implemented by forking an identical copy of the current process (a subshell) with stdout connected to a pipe, let that process run jobs
, and then read the result from the pipe.
This means that any effect jobs
has on the jobs table is restricted to the subshell. The parent doesn't realize you've already seen the "Done" message.
Every time you run [[ $(jobs) ]]
, it'll therefore show you the "Done" message, so that the statement is true.
Adding a jobs &> /dev/null
will cause the parent to update its own jobs table instead of just the subshell's table, and it'll therefore finish correctly.