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

use*_*257 10 bash process-substitution

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

while read -r col0 col1; do
    # [...]
done < <(other_program [args ...])
Run Code Online (Sandbox Code Playgroud)

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

ilk*_*chu 8

至少有一种方法是通过命名管道重定向后台进程的输出.这将允许获取其PID,然后通过PID获得退出状态wait.

#!/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
Run Code Online (Sandbox Code Playgroud)

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

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


mkl*_*nt0 6

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

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: $?"
Run Code Online (Sandbox Code Playgroud)

上面的结果(省略stderr输出):

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: $?"
Run Code Online (Sandbox Code Playgroud)

如您所见,$resultwhile循环中设置的变量可用,并且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"
Run Code Online (Sandbox Code Playgroud)
  • 通过附加的值$?时,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"
Run Code Online (Sandbox Code Playgroud)

上面的结果(省略stderr输出):

result: [/]; exit code: 1
Run Code Online (Sandbox Code Playgroud)