Given this code:
#!/bin/bash
set -euo pipefail
function someFn() {
local input_string="$1"
echo "$input_string start"
sleep 3
echo "$input_string end"
}
function blocking() {
someFn "FOO"
someFn "BAR"
someFn "BAZ"
echo "DONE"
}
function wellBehavedParallel() {
someFn "FOO" &
someFn "BAR" &
someFn "BAZ" &
wait
echo "DONE"
}
function subShellMadness() {
(someFn "FOO" &) &
(someFn "BAR" &) &
(someFn "BAZ" &) &
wait
echo "DONE"
}
echo "BLOCKING_APPROACH"
blocking
echo "WEL_WORKING_PARALLEL"
wellBehavedParallel
echo "THIS DOES NOT WORK"
subShellMadness
It showcases two expected behaviors and one unexpected.
The Blocking one
Simple, executes one line, then the next, slow and boring but solid:
BLOCKING_APPROACH FOO start FOO end BAR start BAR end BAZ start BAZ end DONEThe well-behaved parallel one. All commands
&are executed in parallel, thewaitwaits for all of them to finish, and only then does the main script progress further:WEL_WORKING_PARALLEL FOO start BAR start BAZ start FOO end BAR end BAZ end DONEThis is (at least to me) unexpected, but I assume this is "by design" that once I use subshells, I cannot use
waitin the main script any more.The jobs are still progressed in parallel, but I have lost all control, the main script even ends, and afterwards output is still dumped on the terminal by the subshells:
THIS DOES NOT WORK FOO start BAR start DONE BAZ start philipp@DESKTOP-H0QQ2H8:~$ FOO end BAR end BAZ end
Is there a way from a main script to wait for subshells to finish?
I want to avoid PID-collecting solutions (I know that wait accepts PID as a parameter), yet from what I gather getting the right PID in the first place may be prone to race conditions (since $! will represent the last executed command's PID, not necessarily my command), and I fear PID-reusage could also make such an approach prone to unexpected behavior (am I waiting on my original command, or did some other process take my pid? When calling wait, I seemingly have no way of knowing).
Is there a best practice in dealing with waiting for subshells that reliably waits for them to finish?
(Not using subshells is not an option for me right now.)
waitoutside of them. Thewaitmust occur in the same subshell in which the asynchronous command was started.(someFn "FOO" ) &? That gets you your subshell and a workingwait(someFn "FOO" &) &really useful anyway? Anything you run in the background has to run in a distinct process that only has a copy of the shell environment... i.e. in a subshell. What's wrong with your middle solution?waiton your own children, so there's no danger of referring to an unrelated PID-reusing process unless you started it yourself.(someFn "FOO" & wait) &etc.