These subshells don't wait for their children, and that's why they finish immediately:
(someFn "FOO" &) & (someFn "BAR" &) & (someFn "BAZ" &) &
You get the same problem even if you don't background them:
(someFn "FOO" &) (someFn "BAR" &) (someFn "BAZ" &) If we can change them, then it's simple to have them remain until the children have terminated:
(someFn "FOO" & wait) & (someFn "BAR" & wait) & (someFn "BAZ" & wait) & If that's not possible (e.g. if the commands which abandon their children are not subshells under your direct control) then we can use file locking to observe when their process trees terminate (assuming that they don't close inherited file descriptors):
local lockfile=$(mktemp) exec {fd}>"$lockfile" flock $fd || return 1 (someFn "FOO" &) & # inherits locked $fd (someFn "BAR" &) & (someFn "BAZ" &) & exec {fd}>&- # parent releases lock # now wait for all children to close $fd (by exiting, we hope) flock "$lockfile" rm "$lockfile" This is based on an idea suggested in grawity's answer, somewhat refined to avoid race conditions and allowing use of unmodified child programs.