我正在编写一些 bash 代码,如果出现任何错误,我希望脚本退出。 set -e 技巧效果很好,但不适用于子 shell。这是一个简化的示例:
set -e
chmod a=r file.txt
x=`grep value file.txt | awk '{print $2*2}'`
echo "okay, x=$x"
chmod a= file.txt
x=`grep value file.txt | awk '{print $2*2}'`
echo "should never be printed"
在此示例中,第二个 grep 命令失败,退出状态为非零,但子 shell 仍会执行 awk 命令,该命令以状态零完成。因此,整个子 shell 的退出状态为零,脚本继续执行。
网络搜索显示了几篇有关 set -e 限制的文章,但有人可以推荐一个干净的解决方法吗?
答案1
x=`grep value file.txt | awk '{print $2*2}'`
第二个 grep 命令失败并显示非零退出状态,但子 shell 仍然执行 awk 命令
这实际上与子 shell 没有任何关系,而是与管道的工作方式有关。外壳启动两个都管道中的(所有)命令同时执行,因此在右侧命令开始之前它甚至不知道左侧的退出状态。
是的,默认情况下,管道的退出状态是最右边命令的退出状态。一般来说,这很有用,因为管道也可以完成,以便最右边的命令退出而不读取所有输入,并且当尝试向现在关闭的管道写入更多内容时,左边的命令由 SIGPIPE 信号终止。
这不是一个错误,因为您可能真的不关心其余数据。考虑类似 的东西zcat file.gz | grep -q xyz
,它只查看是否xyz
存在某处在数据中,至少一次。
无论如何,正如他们在评论中所说,您set -o pipefail
在许多 shell(Bash、ksh、zsh、Busybox,至少)中使用以获得最右边的非零退出状态决定整个管道的退出状态。
但请注意,grep
不仅在出现错误时返回错误状态,而且在未找到任何匹配行时也会返回错误状态。如果您不希望这被视为错误,则必须做更多工作。
在这种特殊情况下,您也可以awk
进行模式匹配,例如x=$(awk '/value/ {print $2 * 2}' < file.txt)
。如果无法读取文件,则应该仅因错误而退出。