当字符串是左括号时,shell 括号测试出错

Cla*_*dio 27 shell test

我曾经相信引用字符串总是一个很好的做法,以避免让 shell 解析它。

然后我遇到了这个:

$ x='('
$ [ "$x" = '1' -a "$y" = '1' ]
bash: [: `)' expected, found 1
Run Code Online (Sandbox Code Playgroud)

试图隔离问题,得到同样的错误:

$ [ '(' = '1' -a '1' = '1' ]
bash: [: `)' expected, found 1
Run Code Online (Sandbox Code Playgroud)

我解决了这样的问题:

[ "$x" = '1' ] && [ "$y" = '1' ]
Run Code Online (Sandbox Code Playgroud)

我仍然需要知道这里发生了什么。

Nom*_*mal 25

这是一个非常模糊的极端情况,人们可能会认为测试[内置的定义方式存在错误;但是,它确实与[许多系统上可用的实际二进制文件的行为相匹配。据我所知,它只影响某些情况和具有匹配[运算符(如(!=、等)的值的变量-e

让我解释一下原因,以及如何在 Bash 和 POSIX shell 中解决它。


解释:

考虑以下:

x="("
[ "$x" = "(" ] && echo yes || echo no
Run Code Online (Sandbox Code Playgroud)

没问题; 以上不会产生错误,并输出yes. 这就是我们期望东西工作的方式。'1'如果您愿意,您可以将比较字符串更改为,以及 的值x,它会按预期工作。

请注意,实际/usr/bin/[二进制文件的行为方式相同。如果你运行 eg'/usr/bin/[' '(' = '(' ']'没有错误,因为程序可以检测到参数由单个字符串比较操作组成。

错误发生时,我们与第二表达。第二个表达式是什么并不重要,只要它是有效的。例如,

[ '1' = '1' ] && echo yes || echo no
Run Code Online (Sandbox Code Playgroud)

输出yes,显然是一个有效的表达式;但是,如果我们将两者结合起来,

[ "$x" = "(" -a '1' = '1' ] && echo yes || echo no
Run Code Online (Sandbox Code Playgroud)

当且仅当x(或 时,Bash 拒绝该表达式!

如果我们要使用实际[程序运行上述内容,即

'/usr/bin/[' "$x" = "(" -a '1' = '1' ] && echo yes || echo no
Run Code Online (Sandbox Code Playgroud)

该错误是可以理解的:由于 shell 进行变量替换,/usr/bin/[二进制文件只接收参数( = ( -a 1 = 1和终止],可以理解无法解析左括号是否开始子表达式,其中 涉及and操作。当然,将它解析为两个字符串比较是可能的,但是当应用于带有括号的子表达式的正确表达式时,像这样贪婪地这样做可能会导致问题。

实际上,问题在于 shell[内置的行为方式相同,就好像它x在检查表达式之前扩展了 的值一样。

(这些歧义以及其他与变量扩展相关的歧义是 Bash 实现并现在推荐使用[[ ... ]]测试表达式的一个重要原因。)


解决方法很简单,经常在使用旧shshell 的脚本中看到。您添加一个“安全”字符,通常是x,在字符串前面(两个值都被比较),以确保表达式被识别为字符串比较:

[ "x$x" = "x(" -a "x$y" = "x1" ]
Run Code Online (Sandbox Code Playgroud)


Ale*_*exP 11

[又名test看到:

 argc: 1 2 3 4  5 6 7 8
 argv: ( = 1 -a 1 = 1 ]
Run Code Online (Sandbox Code Playgroud)

test接受括号中的子表达式;所以它认为左括号打开一个子表达式并试图解析它;解析器将=子表达式中的第一件事视为并认为它是隐式字符串长度测试,因此很高兴;然后子表达式后面应该跟一个右括号,而是解析器找到1而不是). 它抱怨。

test正好有三个参数,并且中间的参数是公认的运算符之一时,它将将该运算符应用于第一个和第三个参数,而无需查找括号中的子表达式。

有关完整的详细信息man bash,请查看、搜索test expr

结论: 使用的解析算法test复杂。仅使用简单的表达式并使用shell运算符!, &&and||将它们组合起来。