On Sun, 30 Jun 2024 at 05:08, Zachary Santer <[email protected]> wrote:
> On the other hand, I'm pretty sure
> command-1 | tee >( command-2 ) >( command-3 ) >( command-4 )
> will terminate as soon as command-1 and tee have terminated, but the
> command substitutions could still be running. If you want to run
> commands like this on the command line, it still might be useful to
> know when the command substitutions have terminated.
>
This is a valid concern, and one that has vexed me too.
Broadly I've found two approaches useful:
1. have each command substitution place its own $BASH_PID somewhere that
can be retrieved by the main script, so that an explicit wait $pid will
work; and
2. create a shared output pipe whose only purpose is for something to wait
for EOF.
And then there's a hybrid approach which uses the monitoring pipe to convey
the PIDs and their respective exit statuses, which tends to look like this:
{
save_shopt_lastpipe
shopt -s lastpipe
local -A seq_to_stat=()
local -Ai seq_to_pid=()
{
exec 4>&1 >&3-
echo >&4 S 0 $BASHPID -
cmd1 |
tee >(
echo >&4 S 1 $BASHPID -
cmd2
echo >&4 F 1 $BASHPID $?
) >(
echo >&4 S 2 $BASHPID -
cmd3
echo >&4 F 2 $BASHPID $?
)
echo >&4 F 0 $BASHPID ${PIPESTATUS[0]}
} |
while
IFS=' ' \
read fn seq pid stat
do
seq_to_stat[$cmd]=$stat
seq_to_pid[$cmd]=$pid
done
(( ${#seq_to_stat[@]} == 3 )) || die "Failed to capture initiation of
some command substitutions"
[[ ${seq_to_stat[*]} != *-* ]] || die "Failed to capture termination of
some command substitutions"
wait "${seq_to_pid[@]}" # don't actually care about the statuses of the
subshells, but make sure zombies are cleaned up
restore_shopt_lastpipe
# fill up return PIPESTATUS...
( exit ${seq_to_stat[0]} ) |
( exit ${seq_to_stat[1]} ) |
( exit ${seq_to_stat[2]} )
} 3>&1
That's ~30 static lines plus 4 lines for each substitution; hardly ideal
but it does work without needing any more features added to Bash.
(Realistically, proper error handling would be longer than this anyway, so
it's probably not *that* verbose.
-Martin