繁体   English   中英

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

[英]Get exit code of process substitution with pipe into while loop

以下脚本调用另一个程序在while循环中读取其输出(请参阅Bash - 如何将输入传递给while循环并在循环结束后保留​​变量 ):

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

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

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

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

Bash v4.2 +解决方案:

ccarton的有用答案原则上很有效,但默认情况下, while循环在子shell中运行,这意味着在循环中创建或修改的任何变量对当前 shell都不可见。

Bash v4.2 +中 ,您可以通过打开lastpipe选项来更改此选项 ,这会使管道的最后一段在当前 shell中运行;
在ccarton的回答中, pipefail选项必须设置为$? 反映管道中第一个失败命令的退出代码:

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

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


Bash v3 +解决方案:

ikkachu的有用答案很有效,并展示了先进的技术,但它有点麻烦。
这是一个更简单的替代方案:

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命令的退出代码,向输出没有尾随\\nprintf $? read读取它在最后一个循环的操作,但表示失败(退出代码1),它通常会退出循环。

  • 我们可以用||来检测这种情况 ,并将退出代码(仍然读入$line )分配给变量$ec ,然后退出循环。


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

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"

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

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

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

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

另一种方法是使用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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM