注意:ls -d / /nosuch 用作下面的示例命令,因为它失败(退出代码 1)同时仍然产生 stdout 输出(/)(除了 stderr 输出)。
Bash v4.2+ 解决方案:
ccarton's helpful answer 原则上运行良好,但默认情况下while 循环在 subshell 中运行,这意味着在循环中创建或修改的任何变量对 当前外壳。
在 Bash v4.2+ 中,您可以通过打开 lastpipe 选项 来更改此设置,这会使 最后 段在 current shell 中运行的管道;
正如 ccarton 的回答,必须将 pipefail 选项 设置为让 $? 反映管道中第一个 failing 命令的退出代码:
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'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"
如果命令的输出没有尾随 \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