【发布时间】:2018-10-15 19:40:15
【问题描述】:
我今天在我的一个 shell 脚本中遇到了令人惊讶的行为。如下例所示:
test.sh
#!/bin/bash
do_it() {
shopt -s failglob
{
rm killme.*
echo "and then ..."
} 2>/dev/null || echo "glob error"
echo "life goes on ..."
}
do_it || echo "function failed"
原始脚本中的想法是,我希望允许特定命令发生全局扩展错误,以避免在没有参数时执行该命令,但 检测该错误并采取替代行动。我的期望是当killme.* 不匹配任何东西时,通过
./test.sh || echo "script failed"
会发射
glob error
life goes on ...
或许
function failed
。它没有(使用 Bash 4.2.46)。相反,它打印了
script failed
。在对问题进行故障排除时,我发现了一些更奇怪的事情:如果我通过消除函数来进一步简化脚本,行为就会发生变化。也就是说,考虑这个替代脚本:
test2.sh
#!/bin/bash
shopt -s failglob
{
rm killme.*
echo "and then ..."
} 2>/dev/null || echo "glob error"
echo "life goes on ..."
如果我通过
运行那个./test2.sh || echo "script failed"
,它会打印出来
life goes on ...
当循环调用第一个脚本中的函数时,似乎还有其他一些奇怪的变化,但我还没有完全描述这一点。
问题:
这是记录在案的行为吗?我对 Bash 手册的检查一直没有用。它指定发生“扩展错误”,这似乎很自然,这是一个 shell 错误,而不是命令中的错误,但如果有什么我应该能够预测的细节观察到的结果然后我错过了。
我可以通过在子 shell 中运行扩展来解决这个问题,但是有没有更轻量级的解决方法?我想我可以在未设置
failglob的情况下提前执行扩展,然后测试结果,但这很混乱并且包含竞争条件。
【问题讨论】:
-
总是存在竞争条件。如果有人在 shell 扩展通配符和命令实际运行之间删除了所有匹配项,则会出错。
-
是的,@Barmar,但是在这两种情况下他们会得到不同的错误。特别是,shell 错误还是命令错误会有所不同。
-
使用 'shopt -s nullglob' 可能会有更好的运气......我会在答案中加上上下文,因为 cmets 是有限的 :-)
-
文档说在 POSIX 模式下,“如果发生参数扩展错误,非交互式 shell 将退出”;但我看不出你的两个脚本之间有什么区别会导致 test.sh 使用 POSIX 模式,而 test2.sh 不会。 (并且在本地测试,我发现将
set -o posix添加到test2.sh 或set +o posix到test.sh,不会影响行为。) -
谢谢,@ruakh。我的不是 parameter 扩展错误,所以也许这可以解释为什么 bash 对它的处理与是否启用 POSIX 模式无关。然而,与 bash 不同,POSIX 似乎不区分不同类型的扩展错误。 bash 对我的
test.sh的行为似乎是POSIX-conformant。它与test2.sh的行为,不是。我可能会针对我的特定问题只使用一个子shell,因为这似乎是最安全的。
标签: bash error-handling