我将我的问题缩小到一个让我感到困惑的简单示例。
我已经在 Centos 上用 GNU bash 4.2.46 和在 Ubuntu 上用 4.3.46 测试过它。
这是一个 bash 函数,它在单独调用时返回非零(错误)返回代码,但在我使用 && 或 || 时反转其行为 链接另一个命令。对我来说这看起来像是一个错误。有人可以解释为什么它会这样吗?
$ echo $0
/bin/bash
$ function TEST() {( set -e; set -o pipefail; echo OK; false; echo NOT REACHED; )}
$ type TEST
TEST is a function
TEST ()
{
( set -e;
set -o pipefail;
echo OK;
false;
echo NOT REACHED )
}
$ TEST
OK
$ echo $?
1
$ TEST || echo "NON ZERO"
OK
NOT REACHED
$ echo $?
0
$ TEST && echo "UNEXPECTED"
OK
NOT REACHED
UNEXPECTED
$ echo $?
0
您所看到的是外壳正在执行指定的操作。if
语句和循环中的非零返回码以及||
&&
逻辑运算符不会触发检测set -e
或陷阱。这使得严重错误处理比其他语言更加困难。
所有问题的根源在于,在 shell 中,将非零代码作为有意义的预期状态返回,或者作为命令以不受控制的方式失败的结果返回之间没有区别。此外,shell 的特殊情况将禁用调用堆栈中所有深度的检查,而不仅仅是第一个,完全隐藏嵌套失败set -e
和陷阱(如果你问我,这完全是邪恶的)。
这是一个简短的例子,说明了我的意思。
#!/bin/bash
nested_function()
{
returnn 0 ; # Voluntarily misspelled
}
test_function()
{
if
[[ some_test ]]
then
nested_function
else
return 1
fi
}
set -e
trap 'echo Will never be called' ERR
if
test_function
then
echo "Test OK"
else
echo "Test failed"
fi
第一个函数有一个明显的错误。该函数不包含任何禁用错误检查的内容,但由于它嵌套在一个if
块中(注意,甚至不是直接嵌套),该错误将被完全忽略。
例如,在 Java 中您没有这个问题,其中返回值是一回事,异常是另一回事,并且在if
语句中评估返回值不会阻止调用堆栈中任何级别的异常执行它的工作。您必须try/catch
处理异常,并且无法将异常与返回码混合使用,它们是根本不同的东西(异常可以用作返回值,但不会像抛出时那样触发异常机制)。
如果要在Shell编程中拥有相同的东西,则必须自己构建它。可以使用在所有调用之前使用的“try”函数来完成,并为每个嵌套调用保持状态,一个“throw”等价物允许抛出异常(不是作为非零返回码,而是存储在变量中),并trap ... ERR
拦截非零返回码,并能够执行诸如生成堆栈跟踪和触发受控退出之类的操作(例如删除临时文件、释放其他资源、执行通知)。
使用这种方法,“异常”是显式处理的失败,非零返回码是错误。我想你会牺牲一些性能,实施起来并不容易,而且需要很多纪律。就调试的简易性和您可以在脚本中构建的复杂程度而言,在尝试追踪问题根源时不会不知所措,但它改变了游戏规则。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句