使用管道进入进程替换的退出代码到while循环

时间:2017-05-02 10:48:27

标签: bash process-substitution

以下脚本调用另一个程序在while循环中读取其输出(请参阅Bash - How to pipe input to while loop and preserve variables after loop ends):

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

如何检查other_program的退出代码以查看循环是否正确执行?

4 个答案:

答案 0 :(得分:8)

至少有一种方法是通过命名管道重定向后台进程的输出。这将允许获取其PID,然后通过wait通过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

如果你可以使用直接管道(即不介意在子shell中运行循环),你可以使用Bash的PIPESTATUS,它包含管道中所有命令的退出代码:

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

答案 1 :(得分:6)

注意:ls -d / /nosuch用作下面的示例命令,因为它仍然生成stdout输出(1)时失败(退出代码/)(除了stderr)输出)。

Bash v4.2 +解决方案:

ccarton's helpful answer原则上运行良好,但默认情况下while循环在子shell 中运行,这意味着在循环中创建或修改的任何变量都不可见到当前的 shell。

Bash v4.2 + 中,您可以通过启用 lastpipe选项来更改此设置,这会使最后管道的一部分在当前 shell中运行;
与ccarton的答案一样, pipefail选项必须设置为$?反映管道中第一个失败命令的退出代码:< / p>

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: $?"

以上产量(省略stderr输出):

result: [/]; exit code: 1

如您所见,$result循环中设置的while变量可用,ls命令(非零)退出代码反映在$?

Bash v3 +解决方案:

ikkachu's helpful answer效果很好并且展示了先进的技术,但它有点麻烦 这是一个更简单的选择:

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"
  • 通过将$?命令的退出代码ls的值附加到输出而不会尾随\n ({{1} }),printf $?在最后一个循环操作中读取它,但表示失败(退出代码1),这通常会退出循环。

  • 我们可以使用read检测此案例,并将退出代码(仍然读入||)分配给变量$line然后退出循环。

如果命令的输出没有尾随$ec ,则需要做更多的工作:

\n

以上产量(省略stderr输出):

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"

答案 2 :(得分:2)

一种简单的方法是使用bash pipefail选项传播管道中的第一个错误代码。

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

答案 3 :(得分:1)

另一种方法是使用coproc(需要4.0 +)。

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

coproc使您不必设置异步性和stdin / stdout重定向,否则您需要在等效的mkfifo中进行设置。