Significance of `[…] & wait $!`

What is the significance of the bash pattern [...] & wait $!? For example:

curl -LsS $AZP_AGENT_PACKAGE_LATEST_URL | tar -xz & wait $!

I understand this as:

  1. The & instructs bash to run the prior pipeline in a background subshell.
  2. The wait $! then waits for the pipeline to finish before returning.

But if that’s the case, how is it any different from just running the pipeline itself? What’s the point?

The script containing this example (and a few other instances) can be found at:

https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/docker?view=azure-devops

Asked By: Stanley Yu

||

There are two side effects of running a command asynchronously in a non-interactive shell that make A and A & wait "$!" different:

  • SIGINT and SIGQUIT are ignored for A. If you press Ctrl+C, whilst running bash -c 'sleep 1000 & wait "$!"', you’ll notice bash is terminated, but not sleep.

  • A‘s stdin is redirected to /dev/null:

    $ bash -c 'readlink /dev/fd/0'
    /dev/pts/4
    $ bash -c 'readlink /dev/fd/0 & wait "$!"'
    /dev/null
    

Another difference is that in A | B & wait "$!" (where $! contains the pid of the process eventually running B), the exit status of A is lost ($PIPESTATUS will contain only one entry, the exit status of wait, which will be that of B if the pipefail option is not set).

In zsh, in A | B, zsh waits for both A and B, but in A | B & wait $! only waits for B (and the exit status of A is lost even with pipefail).

Another difference is that if a signal is delivered to the shell, it will be handled straight away and wait will return (even if the process it’s waiting for is not finished).

Compare:

$ bash -c 'trap "echo Ouch" TERM; sleep 10 & wait "$!"; echo "$?"' & sleep 1; kill "$!"
[1] 13749
Ouch
143

Where Ouch is output as soon as SIGTERM is sent to bash (and sleep 10 carries on running after bash terminates) with:

$ bash -c 'trap "echo Ouch" TERM; sleep 10; echo "$?"' & sleep 1; kill "$!"
[1] 13822
$ Ouch                                                                                                                                        6:11
0

[1]  + done       bash -c 'trap "echo Ouch" TERM; sleep 10; echo "$?"'

Where Ouch is output only after sleep 10 returns.

Whether any of those side effects were wanted by the script author is another matter. You’ll notice many other mistakes in that script, it’s very well possible that whoever wrote that script didn’t know much about shell scripting.

Answered By: Stéphane Chazelas
Categories: Answers Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.