help-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Wait for process in pipeline returns wrong exit code


From: Dave Jennings
Subject: Re: Wait for process in pipeline returns wrong exit code
Date: Tue, 1 Jun 2021 23:40:46 +1000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.10.0

Thanks Greg. It's clear now why my original plan won't work. I'll try your suggestion.

Dave.

On 1/6/21 11:29 pm, Greg Wooledge wrote:
On Tue, Jun 01, 2021 at 08:18:09PM +1000, Dave Jennings wrote:
But if I add a pipeline to the initial command:

#!/bin/bash
timeout 5 sleep 10 | cat &

Some background:

In sh, with a pipeline, you only see the exit status of the last
command.  The other exit statuses are simply not available.

Bash adds the PIPESTATUS array variable which exposes the exit statuses
of all the commands in a pipeline, but it only works with foreground
pipelines.

Now you've gone and made a *background* pipeline, and there's simply
no support for exposing the exit statuses of the internal pieces of
one of those.

parent_pid=$( ps --pid $! -o ppid --no-headers )
timeout_pid=$( pgrep --parent $parent_pid timeout )

So now you're trying to hack together your *own* support for extracting
the exit statuses of internal pieces of background pipelines.

wait $timeout_pid
echo "exit code: $?"

The wait command only works on direct child processes of the shell, a.k.a.
"asynchronous commands".  In the case where your asynchronous command is
a pipeline, we fall back to the original sh feature set.  The exit status
of your asynchronous command is that of the last command in the pipeline,
and the other commands' exit statuses are simply discarded.  There's no
PIPESTATUS variable for this case, at least not in the parent shell.  All
of the action is taking place in the subshell.

So, really, what you want to do is extract the exit statuses of all the
commands in a *background* pipeline, and expose them to the parent shell.
We just need to come up with a way to do that.

The most obvious way is to use a temp file.

=======================================================================
unicorn:~$ cat foo
#!/bin/bash

unset tempfile
trap '[[ $tempfile ]] && rm -f "$tempfile"' EXIT
tempfile=$(mktemp) || exit

echo "Starting pipeline"
{
   false | true | sleep 2 | exit 14 | echo foo | cat
   echo "${PIPESTATUS[@]}" > "$tempfile"
} &
wait
echo "Pipeline finished.  Results:"
cat "$tempfile"
unicorn:~$ ./foo
Starting pipeline
foo
Pipeline finished.  Results:
1 0 0 14 0 0
=======================================================================

The parent shell process can't see the PIPESTATUS variable of the child
subshell, but it can read the results from the temp file after the child
has written them there.

If you think you can come up with a better alternative, by all means,
go for it.  This is what I would use.




reply via email to

[Prev in Thread] Current Thread [Next in Thread]