Bash – Would someone provide an example of a Bash synchronous vs asynchronous job

bash

According to the Bash 4.4 changelog:

https://lists.gnu.org/archive/html/info-gnu/2016-09/msg00008.html

There are a few incompatible changes between bash-4.3 and bash-4.4.
Bash now retains the exit status only of asynchronous jobs, as opposed
to all jobs. This means that it is not possible to use `wait' to
retrieve the status of a previously-completed synchronous command.

https://fossies.org/diffs/bash/4.3.46_vs_4.4/CHANGES-diff.html

Bash only adds asynchronous commands to the table of background pids
whose status it remembers, to avoid it growing too large during
scripts that create and reap large numbers of child processes. This
means that `wait' no longer works on synchronous jobs, but $? can be
used to get the exit status in those cases.

I've already been bitten by some other breaking changes between 4.3 and 4.4 but I don't know how to write an example which tests this particular change.

What is the difference between a synchronous job versus an asynchronous one in Bash and where did it store a table of pids to be queried by wait?

Best Answer

Example:

4.3

bash-4.3$ (echo "$BASHPID"; exit 123)
5358
bash-4.3$ wait 5358; echo "$?"
123

4.4

bash-4.4$ (echo "$BASHPID"; exit 123)
12171
bash-4.4$ wait 12171
bash: wait: pid 12171 is not a child of this shell

You can no longer use wait to get the exit status of that subshell that run in foreground (synchronously). foreground refers to jobs of interactive shells, but the same applies for commands run in non-interactive shells.

Note that it also applies to background jobs that are later put in foreground with fg:

bash-4.4$ (sleep 10; exit 123) &
[1] 12857
bash-4.4$ fg
( sleep 10; exit 123 )
bash-4.4$ wait 12857
bash: wait: pid 12857 is not a child of this shell

With bash-4.3 and before bash would remember the exit status of every past background and foreground command. That would not be useful for foreground commands as usually, you don't know their pid in the script and also in things like:

cmd1 &
...
cmd2
wait "$!"

cmd1's pid could very well have been reused for cmd2. In that case wait "$!" would get you the exit status of cmd2 instead of cmd1. Recording the pid of only asynchronous commands slightly reduces the risk of wait giving you the exit status of the wrong command (beside the performance issue mentioned by @Christopher).

Related Question