Get exit code of process substitution with pipe into while loop

泪湿孤枕 提交于 2019-11-27 18:01:38

问题


The following script calls another program reading its output in a while loop (see Bash - How to pipe input to while loop and preserve variables after loop ends):

while read -r col0 col1; do
    # [...]
done < <(other_program [args ...])

How can I check for the exit code of other_program to see if the loop was executed properly?


回答1:


Note: ls -d / /nosuch is used as an example command below, because it fails (exit code 1) while still producing stdout output (/) (in addition to stderr output).

Bash v4.2+ solution:

ccarton's helpful answer works well in principle, but by default the while loop runs in a subshell, which means that any variables created or modified in the loop will not be visible to the current shell.

In Bash v4.2+, you can change this by turning the lastpipe option on, which makes the last segment of a pipeline run in the current shell;
as in ccarton's answer, the pipefail option must be set to have $? reflect the exit code of the first failing command in the pipeline:

shopt -s lastpipe  # run the last segment of a pipeline in the current shell
shopt -so pipefail # reflect a pipeline's first failing command's exit code in $?

ls -d / /nosuch | while read -r line; do 
  result=$line
done

echo "result: [$result]; exit code: $?"

The above yields (stderr output omitted):

result: [/]; exit code: 1

As you can see, the $result variable, set in the while loop, is available, and the ls command's (nonzero) exit code is reflected in $?.


Bash v3+ solution:

ikkachu's helpful answer works well and shows advanced techniques, but it is a bit cumbersome.
Here is a simpler alternative:

while read -r line || { ec=$line && break; }; do   # Note the `|| { ...; }` part.
    result=$line
done < <(ls -d / /nosuch; printf $?)               # Note the `; printf $?` part.

echo "result: [$result]; exit code: $ec"
  • By appending the value of $?, the ls command's exit code, to the output without a trailing \n (printf $?), read reads it in the last loop operation, but indicates failure (exit code 1), which would normally exit the loop.

  • We can detect this case with ||, and assign the exit code (that was still read into $line) to variable $ec and exit the loop then.


On the off chance that the command's output doesn't have a trailing \n, more work is needed:

while read -r line || 
  { [[ $line =~ ^(.*)/([0-9]+)$ ]] && ec=${BASH_REMATCH[2]} && line=${BASH_REMATCH[1]};
    [[ -n $line ]]; }
do
    result=$line
done < <(printf 'no trailing newline'; ls /nosuch; printf "/$?")

echo "result: [$result]; exit code: $ec"

The above yields (stderr output omitted):

result: [no trailing newline]; exit code: 1



回答2:


At least one way would be to redirect the output of the background process through a named pipe. This would allow to pick up its PID and then get the exit status through waiting on the PID.

#!/bin/bash
mkfifo pipe || exit 1
(echo foo ; exit 19)  > pipe &
pid=$!
while read x ; do echo "read: $x" ; done < pipe
wait $pid
echo "exit status of bg process: $?"
rm pipe

If you can use a direct pipe (i.e. don't mind the loop being run in a subshell), you could use Bash's PIPESTATUS, which contains the exit codes of all commands in the pipeline:

(echo foo ; exit 19) | while read x ; do 
  echo "read: $x" ; done; 
echo "status: ${PIPESTATUS[0]}" 



回答3:


A simple way is to use the bash pipefail option to propagate the first error code from a pipeline.

set -o pipefail
other_program | while read x; do
        echo "Read: $x"
done || echo "Error: $?"



回答4:


Another way is to use coproc (requires 4.0+).

coproc other_program [args ...]
while read -r -u ${COPROC[0]} col0 col1; do
    # [...]
done
wait $COPROC_PID || echo "Error exit status: $?"

coproc frees you from having to setup asynchronicity and stdin/stdout redirection that you'd otherwise need to do in an equivalent mkfifo.



来源:https://stackoverflow.com/questions/43736021/get-exit-code-of-process-substitution-with-pipe-into-while-loop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!