我可以在 bash 中使 [ 语法错误吗 if 语句导致 shell 退出并出现错误?

Can I make bad [ syntax in a bash if statement cause the shell to exit with an error?

提问人:Seriously 提问时间:8/23/2023 最后编辑:Charles DuffySeriously 更新时间:10/9/2023 访问量:67

问:

请考虑以下代码:bash

if [ "$a" = "foo"]
then
  echo "TRUE"
fi
echo $?

我们得到:

bash: [: missing `]'
0

所以基本上,if 中的测试由于拼写错误(和 之间没有空格)而失败,但整个的退出代码仍然是 aka。成功。"foo"]if0

这个答案解释了为什么退出代码是(我们得到的退出代码是因为什么都没有)。0if0true

到目前为止一切顺利,但这种行为非常危险,因为它可能导致静默错误。有没有办法重写条件,或更改任何设置,使这样的拼写错误触发脚本 / 的非零退出?bashìf

bash if-语句 退出代码

评论

2赞 Barmar 8/23/2023
我不这么认为。 不是语法的一部分,它只是作为测试运行的命令。[if
1赞 Barmar 8/23/2023
if无法区分由于语法错误或条件为 false 而失败。[
1赞 Charles Duffy 8/24/2023
@Seriously,但这不是语法错误:这是完全正确的 bash 语法。 有自己的语法,与 bash 的语法不同——虽然在实践中 bash 提供了自己的命令实现,但这只是一个性能优化;如果在语言层面上表现得不同,那么你就会重新发明。[[/usr/bin/[[[[
1赞 Charles Duffy 8/24/2023
作为一个现实世界的实用解决方案,不需要人们重新构建具有数十年向后兼容性要求的东西(更不用说需要遵守文字标准-机构批准的标准),巩固了他们当前的设计:使用内置 shellcheck 支持的文本编辑器。就我个人而言,我将 Helixbash-language-server 一起使用,因此当我编写带有手头错误的代码时,我的编辑器会立即标记它。
1赞 Charles Duffy 8/24/2023
(如果你想让 Bash 能够区分错误的测试结果和错误的测试语法,也许你应该停止使用并切换到 Exclusive Use; 不是标准化的,所以 bash 可以在不破坏标准/向后兼容/等的情况下改变它的行为,并且错误的语法已经被认为是错误的 bash 语法;的行为并不是一成不变的,但它受到严格限制,使得“嘿,这是我刚刚有的这个好主意!”的变化通常难以置信。[[[[[[[[

答:

1赞 Barmar 8/23/2023 #1

if不区分失败的不同原因。但是,它会根据条件是否失败或存在其他错误来设置不同的退出状态。退出状态将是当条件失败时,错误将大于 1。[1

因此,您可以明确检查以区分:$?

[ "$a" = "foo"] 2>/dev/null
case $? in
    0) echo "TRUE" ;;
    1) ;;
    *) (exit 1) ;;
esac
echo $? # prints 1

评论

1赞 Barmar 8/24/2023
我已将输出重定向到以抑制它。/dev/null
2赞 4 revsCharles Duffy #2

是的,有一种方法可以重写条件:不要使用 [.

#!/bin/bash -e
if [[ "$a" = "foo"]]
then
  echo "TRUE"
fi
echo "REACHED"

正确结果:

yourscript: line 1: syntax error in conditional expression

您可以在 https://ideone.com/ZH1Znb 上看到这一点


或者,您可以检查退出状态:

#!/bin/bash -e

# using legacy ksh syntax because some versions of bash are picky
# about function names with POSIX-y syntax; in new bash, though,
# this also works with `[() {` as the first line.
function [ {
  builtin [ "$@" || {
    rc=$?
    case $rc in
      0|1) return $?;;
      *) echo "ERROR: Bad test syntax" >&2; exit "$rc";;
    esac
  }
}

if [ "$a" = foo]
then
  echo "TRUE"
fi
echo "REACHED"

但这显然是一种可怕的可憎之物。


请注意,虽然上面的例子使用了 ,但这个功能有严重的缺陷,我强烈建议不要使用它set -e

0赞 Kaz 8/24/2023 #3

这实际上不是语句的语法错误,而是命令的语法错误。由于找不到结束参数,该命令失败。if[]

所以你在这里所拥有的相当于:

if false; then
  echo "TRUE"
fi

测试命令的失败终止状态不会传播到 。if

你只需要一个:else

if command_that_fails_for_any_reason ; then
   echo "okay"
else
   false 
fi

现在,这将导致命令处于失败终止状态,因此脚本将进入模式。falseset -e

如果 test 命令的失败状态传播到 ,则意味着无法阻止脚本在模式下终止。ififset -e

在以下示例中,如果失败,脚本将死亡,尽管脚本对其终止状态做出反应。checked_command

set -e
unchecked_command #  this will terminate the script if it fails

if checked_command ; then   # the script is testing this one
  : ...
fi

我们可以争辩说,这里的错误处理是不完整的,因为它只是针对成功的案例进行测试。然而,根据 shell 语言和功能的设计,如果脚本测试终止状态,则认为它已承担全部责任。ifset -e

在以下情况下,让剧本保释会很烦人:

if grep -s pattern /some/file ; then
   : # react to pattern being found in file
fi