Bash: 带有多个参数的测试(内置):它们是如何解析的?

Bash: test (builtin) with several arguments: how are they parsed?

我还没有为 bash 内置的 test / [ 命令使用过这个特殊的结构,但我今天 运行 进去了,我很困惑。它看起来像这样:

[ -n "${FOO}" -a -r ${FOO}/bar ] && echo OK

我知道每个开关单独做什么,但我不确定它们像这样分组时的行为。具体来说, -a -r [operand] 部分。 man 页面的内容如下:

test and [ evaluate conditional expressions using a set of rules based on the number of arguments.

0 arguments
The expression is false.

1 argument
The expression is true if and only if the argument is not null.

2 arguments
If the first argument is !, the expression is true if and only if the second argument is null. If the first argument is one of the unary conditional operators listed above under CONDITIONAL EXPRESSIONS, the expression is true if the unary test is true. If the first argument is not a valid unary conditional operator, the expression is false.

3 arguments
The following conditions are applied in the order listed. If the second argument is one of the binary conditional operators listed above under CONDITIONAL EXPRESSIONS, the result of the expression is the result of the binary test using the first and third arguments as operands. The -a and -o operators are considered binary operators when there are three arguments. If the first argument is !, the value is the negation of the two-argument test using the second and third arguments. If the first argument is exactly ( and the third argument is exactly ), the result is the one-argument test of the second argument. Otherwise, the expression is false.

4 arguments
If the first argument is !, the result is the negation of the three-argument expression composed of the remaining arguments. Otherwise, the expression is parsed and evaluated according to precedence using the rules listed above.

5 or more arguments
The expression is parsed and evaluated according to precedence using the rules listed above.

好的,很好。由于我提供了 5 个参数,表达式将被解析并应用规则。我假设它将分为两部分进行拆分和评估,如下所示:

[ -n "${FOO}" ] && [ -a -r ${FOO}/bar ] && echo OK

但事实并非如此,因为这会产生 [: -r: binary operator expected,它不喜欢 [ -a -r ... ]。当然,这有效:

[ -n "${FOO}" ] && [ -a ${FOO}/bar ] && [ -r ${FOO}/bar ] && echo OK

这也是:

[ -n "${FOO}" -a ${FOO}/bar ] && echo OK

但这失败了:

[ -n "${FOO}" -r ${FOO}/bar ] && echo OK

让我感到困惑的是这样的部分:

The -a and -o operators are considered binary operators when there are three arguments

-a 是如何作为二元运算符处理的?我相信它会测试文件的存在,但它在这里有其他行为吗?它对第二个 ope运行d 有什么作用? Bash 内置函数 test 如何解析原始示例中的参数?我错过了什么?

解析后不需要 -a,因为 && 取代了它。等效的解析表达式是

[ -n "${FOO}" ] && [ -r ${FOO}/bar ] && echo OK

听从您的指挥

test -n "${FOO}" -r ${FOO}/bar 

你有两个条件,但你没有告诉 test 是否两个都需要为真,或者一个为真是否足够,这就是你得到错误的原因。因此你必须写一个

test -n "${FOO}" -a -r ${FOO}/bar 
test -n "${FOO}" -o -r ${FOO}/bar 

听从您的指挥

-n "${FOO}" -a ${FOO}/bar 

你也有两个条件,但在这里你明确地说(使用-a)两个都需要为真。所以这没问题。

这个问题的其他答案都是正确的,但我会添加我自己的答案,以便首先直接解决导致我困惑的确切原因。

这适用于 Bash 内置函数,也适用于(对示例进行一些细微更改)BSD /bin/test。

test / [(我将使用 test 来指代两者)命令实现了一个本质上不明确的语法:它将接受开关 -a-o,但它们的含义取决于上下文。当用作两个布尔操作数之间的连接词时,-a 充当逻辑 AND。但是当用作一元运算符时,它会检查下一个参数中给定的文件名是否存在。同样,-o 被视为逻辑 OR,也被视为是否启用 shell 选项的测试。

这为一些看起来很奇怪的表情打开了大门。正如@user1934428 在回答下的评论中所讨论的那样,有这个:

# Assuming that if ${FOO}/bar exists, then it is readable: 

[ -n "${FOO}" -a -r "${FOO}"/bar ] 

# is exactly the same as either of the following:

[ -n "${FOO}" -a -a "${FOO}"/bar ]
[ -n "${FOO}" ] && [ -a "${FOO}"/bar ]

也许这看起来微不足道?也许你渴望混乱?我什至不打算尝试破译这些:

# Assuming that ${FOO}/bar exists and is readable, This returns 1:

[ -n "${FOO}" -a -a -a -a "${FOO}"/bar ]

# and this returns 0:

[ -n "${FOO}" -a -a -a -a -o -o -o "${FOO}"/bar ]

我从中得出的结论很简单:在使用 test 时坚持使用基本且明确的结构。不要戳睡熊