3

I have some conditions for some background job to run:

condition-command && condition-command && background-job &

The problem is: I want the conditions to block until the job runs, as if I had run:

condition-command; condition-command; background-job &

But it isn't a condition, if previous command fails I do not want the job to run.

I realised it is asynchronous, but it should not, in my mind the both following scripts should be the same, but they do not:

sleep 2; echo foo & sleep 1; echo bar; wait   # prints foo, then bar: correct
sleep 2 && echo foo & sleep 1; echo bar; wait # prints bar, then foo: bug

I know if I test $? variable it would work, or if I put the last one inside a subshell (but then I would lost job controls, I want to avoid daemons), but I want to know why bash make it this way, where is it documented? Is there any way to prevent this behaviour?

Edit: Chained ifs is disgusting, that is why I will not accept it as a alternative way.
Edit 2: I know a subshell is possible, but it will not work for me, let us imagine I want to run a bunch of commands then wait in the end. It will be possible if i check the existence of /proc/$PID directory, but it would be a pain in the neck if there are several jobs.
Edit 3: The main question is WHY bash does it, where is it documented? Whether or not it has a solution is a bonus!

  • In your example is foo supposed to be a background job? – Seth Nov 21 '14 at 19:03
  • @Seth, yes, it will increase performance if I run in parallel what I am able to. – Tiago Pimenta Nov 21 '14 at 19:08
  • I know a subshell is possible, but it will not work for me [...]. Your last command will start at subshell anyway, from bash manual: If a command is terminated by the control operator &, the shell executes the command in the background in a subshell. – jimmij Nov 21 '14 at 19:25
  • Yeah, but it's a subshell whose PID and job no. are known to the current shell: (background-job&) leaves it completely detached – Useless Nov 21 '14 at 19:28
  • How about using {} then: { background-job& }? – jimmij Nov 21 '14 at 19:32
3

If you don't want the background to apply to the whole line, then use eval:

sleep 2 && eval 'sleep 10 &'

Now only the second command will be a background job, and it will be a proper background job that you can wait on.

  • It is subshell, the last one will become a daemon. If you read till the end my question, I already knew this would be possible, but I cannot dispose the job controls, see Edit 2 – Tiago Pimenta Nov 21 '14 at 19:14
  • @TiagoPimenta, ahh, I see now... edited with fix. – psusi Nov 21 '14 at 19:23
  • While this simple example works, eval is absolutely not the right tool. – Gilles Nov 21 '14 at 23:45
  • @Gilles, why not? Got a better idea? – psusi Nov 21 '14 at 23:50
1

After your edit: the issue is that && has higher precedence than &, so the whole thing binds together into an AND list, and gets backgrounded as a unit. See Lists of Commands in the manual, although it isn't enormously clear.

The smallest change to your original code that would work is

if condition-command && condition-command; then background-job & fi

(it is an if, but not a chained one).


Just

condition-command && condition-command && (background-job &)

should do the trick.

  • 1
    It is subshell, the last one will become a daemon. If you read till the end my question, I already knew this would be possible, but I cannot dispose the job controls, see Edit 2 – Tiago Pimenta Nov 21 '14 at 19:15

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.