我正在玩 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
永远不会运行。