5

If do this on my bash terminal:

(
    (sleep 0.5 && echo 'first command, second result' &);
    (echo 'second command, first result' &);
)

then I see:

second command, first result
myprompt$
first command, second result

Now, I want to wait for all of my subshells to finish.

How do I achieve that?

I know that I could add wait at the end of each subshell command:

(
  (sleep 0.5 && echo 'first command, second result' & wait);
  (echo 'second command, first result' & wait)
)
first command, second result
second command, first result
myprompt$

and seemingly I get my result (I don't, as the order is reversed, and I think the subshells are not waited upon).

I also thought I could do this:

(
  (sleep 0.5 && echo 'first command, second result') &
  (echo 'second command, first result') &
  wait
)
second command, first result
first command, second result
prompt$

but this is accidentally changing the CONTENT of the subshell.

I actually want to be able to wait for this as well:

 (
  (sleep 0.5 && echo 'first command, second result' &) &
  (echo 'second command, first result' &) &
  wait
)
second command, first result
prompt$
first command, second result

So this too won't wait properly.

My example here is contrived; in reality I do not control the bash script that my outer subshell may execute.

The bash scripts gets dynamically constructed and it should be as forgiving to improper bash code as possible; at least for now.

The outer shell, if possible, should be able to determine if the subshells have ended.

Is this possible, and if so how?

I can freely control the outer shell and add any command there. I cannot modify the code within the subshells.

8
  • Related / somewhat of a follow up to my question: unix.stackexchange.com/q/798660/12471 Commented Sep 8 at 12:43
  • Voted to reopen: My solution I thought I had was not correct. The marked as duplicate answers don't deal with subshells. Commented Sep 8 at 14:34
  • 1
    I’ve reopened this question. For the record the reference question was Bash wait for all subprocesses of script, and while one of the answers mentions the problem with background tasks started from a subshell, it doesn’t provide a solution. Commented Sep 8 at 14:46
  • A process can only wait for background processes that are its immediate children. When you run the background processes from subshells, that breaks the link with the original shell and it can't wait for them. Commented Sep 8 at 15:05
  • 1
    The only solution I can think of would be to have the subshells each wait for their background processes and then write something to a file or pipe, and then have the main shell wait for the messages in that file/pipe. Commented Sep 8 at 15:08

1 Answer 1

4

While I said I don't want to change the content of the subshell, I was able to make it work by injecting a trap 'wait' EXIT at the beginning of each subshell.

That way, I am not changing the script, and its behavior per se, but I ensure it waits until the end:

(
  trap 'wait' EXIT
  (
    trap 'wait' EXIT
    sleep 0.5 && echo 'first command, second result' &
  ) &
  (
    trap 'wait' EXIT
    echo 'second command, first result' &
  ) &
)
second command, first result
first command, second result

So in my dynamic script generation script, I basically now inject

trap 'wait' EXIT at the start of each subshell, and for existing subshells, I inject trap 'wait' EXIT at the very beginning.

It seems to work alright, though I am unsure if there may be some unwanted side effects.

One such side effect: Error state may be lost and command may be reported as successful.


Note, be careful that this is not the same, as this will produce the output in the wrong order:

(
  trap 'wait' EXIT;
  (trap 'wait' EXIT;sleep 0.5 && echo 'first command, second result' &)
  (trap 'wait' EXIT;echo 'second command, first result' &)
)
first command, second result
second command, first result
11
  • clever! Note that to me multiple levels of trapped EXIT tells me that shell programming is really the wrong tool for this kind of task. Commented Sep 8 at 18:09
  • 1
    What's the difference between using trap wait EXIT and just putting wait at the end of that subshell? Commented Sep 8 at 18:11
  • 1
    @Barmar Good question. In the end, I found it easier to prepend code to a subshell, than to deal with whatever line ending code within a subshell may have. Lines may end with & or ; or simply don't have a char in the end. I actually tried injecting wait yet that was prone to these kind of errors where commands suddenly became echo "foo bar" &; that would then fail with -bash: syntax error near unexpected token ;'`. Commented Sep 8 at 18:52
  • 1
    @MarcusMüller For those who come after: What more sensible PHP-multi-job-control-alternatives are you referencing? Commented Sep 9 at 13:13
  • 1
    @k0pernikus just: PHP? Like, PHP has threads and aside from the heavily deprecated exec function (that badly wraps the system libc call, not exec as the name suggests) it offers popen() and proc_open(), which gives you handles on which you can actually wait with select, for example, and you can then in plain PHP have a point in your program where all threads that you started to proc_open/popen external programs have finished. (this is basic PHP from 2006 that I learned on the side to help a classmate, I'm sure there's much better tools and liibraries now) Commented Sep 9 at 13:21

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.