1

I'm trying to loop until a background process (launched earlier in the script) is complete. An easily reproducible test case is:

ping -c 10 localhost &>/dev/null &

On the command line, I can loop while [[ -n $(jobs) ]] (while $(jobs) is not null).

$ ping -c 10 localhost &>/dev/null &
[1] 19078
$ while [[ -n $(jobs) ]]; do echo -n .; sleep 1; done
.........[1]+  Done                    ping -c 5 localhost &> /dev/null

However, the same two lines in a script will keep printing .s until I hit Ctrl-C.

Weirdly, if I call jobs inside the loop, the script ends as expected.

$ ./background-ping.sh
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Done                    ping -c 5 localhost &> /dev/null

I am aware that there are other ways to check if the background job is complete (e.g. checking /proc), but I want to know why checking jobs doesn't work as expected.

2

If I understand your problem, your 2 lines won't stop in a script.

That's what I made in mine:

ping -c 5 google.com &>/dev/null &
while [[ -n $(jobs -r) ]]; do echo -n "."; sleep 1; done

jobs -r checks running processes, and calling my script works as expected, the script stops when ping is done.

EDIT: I think that in a script the parent process is known as a running process and so that's why jobs continue to think that there is a running process. That's an hypothesis

Is that the answer ? (or maybe I really don't understand what you mean, my poor english can be the problem...)

  • Thanks! I'm still not sure why just jobs doesn't work, but this does work. – stephenwade Mar 24 '15 at 1:15
  • I edited my answer, I think that the script is known as a "stopped" process. I checked what's append with -r option that only checks running process. It's seems like a bug BTW... maybe you've found a real problem that can be sent to "jobs" author :) – Metal3d Mar 24 '15 at 1:19
1

Whenever you have issues like this, you should always try printing your variables so you understand what's going on. In this case, jobs also returns finished jobs. If you run this:

ping -c 5 localhost &>/dev/null &
while [[ -n $(jobs | tee -a temp) ]]; do
    echo -n .; 
    sleep 1; 
done

You will see the following output in temp:

[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[...]

So, the output of jobs here is never empty. Even when the job is finished, jobs still returns the Done message. That's why Metal3d's solution using jobs -r worked.

What is more confusing is why running jobs within the loop causes it to work correctly. The answer will be something to do with the fact that while [[ -n $(jobs) ]] runs jobs in a separate subshell but I'm not sure about the details. I have posted a question about this on U&L if anyone is interested.

  • That's what I was trying to do by calling jobs inside the script. I didn't know about tee, though – thanks! – stephenwade Mar 24 '15 at 15:11
  • @stephenwade by the way, my question on U&L has been answered and it is indeed down to subshells. The jobs that runs inside the $() gets a copy of its parent shell's variables. Those are not changed until you run an extra jobs within the loop and that will then update the state which is passed to the next launching of $(). – terdon Mar 24 '15 at 15:14
0

You can use wait command that waits for background jobs to be stopped

Example:

$ ping -c 10 localhost &>/dev/null &
[29787]
$ wait
[1]+  Done ping -c 10 localhost &>/dev/null

The wait command blocks and when ping has finished, prompt is released then prints a message.

You can use is with several commands:

$ ping -c 5 localhost &>/dev/null & ping -c 5 facebook.com  &>/dev/null &
[1] 29867
[2] 29868
$ wait
[1]-  Done                    ping -c 5 localhost &>/dev/null
[2]+  Done                    ping -c 5 facebook.com &>/dev/null
  • As seen in the code examples, I want to print periods to indicate that the process is ongoing, so the wait command won't work here. – stephenwade Mar 24 '15 at 0:56
  • "I am aware that there are other ways to check if the background job is complete (e.g. checking /proc), but I want to know why checking jobs doesn't work as expected." – stephenwade Mar 24 '15 at 0:58
  • Ok I see what you mean, I think I've got solution. Let me try something and I'll tell you – Metal3d Mar 24 '15 at 1:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.