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.