如何在 Bash 脚本中同时获取命令的结果及其返回代码?

如何在 Bash 脚本中同时获取命令的结果及其返回代码?

我正在玩 Bash 脚本。我想将命令的结果分配给一个变量,并将其返回代码分配给另一个变量。

前任。:

line_count=$(cat "$filename" | wc -l) #If the file does not exist, an error code is returned
cmd_return_code=$? #I want to assign the previous error code to a variable

我尝试使用$?如上所示的方法,但它捕获了前一个分配本身的返回代码,即 0。

我怎样才能做到这一点?

谢谢

答案1

这里的问题是,在管道中cat "$filename" | wc -l,当文件不存在时cat将以错误退出,但wc -l将成功计算从接收到的 0 行文本cat。管道中最后一个命令的退出状态被视为整个管道的最终状态,因此整个管道被视为成功。像这样:

$ cat nosuchfile | wc -l
cat: nosuchfile: No such file or directory
       0
$ echo "$?"
0

通常在 bash 中,你可以使用数组获取管道中各个命令的状态PIPESTATUS,如下所示:

$ cat nosuchfile | wc -l
cat: nosuchfile: No such file or directory
       0
$ echo "${PIPESTATUS[@]}"
1 0

...但是这不适用于命令扩展中的管道$( ),因为该管道将在子 shell 中执行,并且PIPESTATUS不能从子 shell 传播;只有最终状态会传回父 shell:

$ line_count=$(cat nosuchfile | wc -l)
cat: nosuchfile: No such file or directory
$ echo "${PIPESTATUS[@]}"
0

那么,你能做什么呢?好吧,正如 l0b0 所说,一种可能性是设置选项pipefail。你不必对整个脚本执行此操作,你可以通过在命令替换中执行此操作来仅为该特定子 shell 设置它:

$ line_count=$(set -o pipefail; cat nosuchfile | wc -l)
cat: nosuchfile: No such file or directory
$ echo "$?"
1

对于这个特定的命令,你也可以消除管道(这就是所谓的cat 或 UUOC 的无用用法),并wc直接从文件中读取,方法是将文件名作为参数传递:

$ line_count=$(wc -l nosuchfile)
wc: nosuchfile: open: No such file or directory
$ echo $?
1

...或者使用输入重定向:

$ line_count=$(wc -l <nosuchfile)
-bash: nosuchfile: No such file or directory
$ echo $?
1

这两个选项之间有几个区别:如果你将文件名传递给wc(并且它存在),它将输出文件名以及行数:

$ line_count=$(wc -l realfile.txt)
$ echo "$line_count"
       6 realfile.txt

...而使用重定向选项则不会。另外,使用第二个选项时,shell 负责打开文件(并将打开的文件句柄交给命令wc),因此如果失败,shell 会给出错误(请注意,错误消息来自“-bash”,而不是wc),并且wc永远不会运行。

答案2

退出代码的分配命令替换是命令替换的退出代码。例如,参见的退出代码foo="$(false)"

要使管道的退出代码成为管道中第一个失败命令的退出代码:

set -o pipefail

相关内容