Bash测试运算符[[... -eq ...]]中的错误或功能?

时间:2018-05-07 08:01:33

标签: bash

有人可以解释一下之间的区别:

VAR=1xyz && [[ $VAR -eq $VAR ]] 2>/dev/null && echo "Yes, VAR = $VAR is an integer" || echo "No, VAR = $VAR is NOT an integer"
No, VAR = 1xyz is NOT an integer

VAR=xyz1 && [[ $VAR -eq $VAR ]] 2>/dev/null && echo "Yes, VAR = $VAR is an integer" || echo "No, VAR = $VAR is NOT an integer"
Yes, VAR = xyz1 is an integer

这是Bash中的错误或功能吗?

如果不使用[[ ... ]]我使用[ ... ],我会得到$VAR 的预期结果。

1 个答案:

答案 0 :(得分:3)

要了解这里发生了什么,你需要明确两件事:

1。条件结构的确切含义

在大多数语言中,存在某种可被解释为真或假的值。这可能是一个布尔数据类型,一个整数(其中0是假,其他一切都是真的)或者某些概念"真实性"这是按类型实现的。

但Posix炮弹没有" truthy"和" falsey"值。他们所拥有的是可能成功或失败的陈述。什么"成功"和"失败" mean主要取决于命令本身来确定,但bash本身会将某些行为归类为失败。例如,如果shell无法确定命令名所指的内容,则会将该命令视为失败:

$ undefined_command && echo Yes || echo No
undefined_command: command not found
No

此外,如果命令被信号终止,例如分段错误,则shell会将其视为失败:

$ ./segfault && echo Yes || echo No
Segmentation fault (core dumped)
No

但即使错误并非致命,许多命令也会发出失败信号。 (它们通过将其状态设置为非零值来执行此操作。)例如,如果任何文件名参数不存在,ls将返回失败(即使其他文件参数也存在):

$ ls no_file exists && echo Yes || echo No
ls: cannot access 'no_file': No such file or directory
-rw-rw-r-- 1 rici rici 0 May  7 13:13 exists
No

如图所示,通常(但并不总是)会向stderr打印一条错误消息,其中提供了有关失败原因的一些提示。如果您想让自己感到困惑,通常可以禁止显示错误消息:

$ undefined_command 2>/dev/null && echo Yes || echo No
No
$ ls no_file exists 2>/dev/null && echo Yes || echo No
-rw-rw-r-- 1 rici rici 0 May  7 13:13 exists
No

这正是你在原始问题中所做的。如果我们不隐藏错误消息,那么发生的事情会变得更加明显:

$ VAR=1xyz && [[ $VAR -eq $VAR ]] && echo Yes || echo No
bash: [[: 1xyz: value too great for base (error token is "1xyz")
No
$ VAR=xyz1 && [[ $VAR -eq $VAR ]] && echo Yes || echo No
Yes

换句话说,尝试将字符串1xyz用作数字(因为-eq 数字相等)会产生错误,这会被视为失败。但是,字符串xyz1是有效的数值。我们将在下一节中了解为什么会出现这种情况。

但在我们开始之前,我们需要注意[[ ... ]]命令(尽管是bash扩展名),而不是shell没有布尔值的规则的例外值。与任何其他命令一样,[[可以成功或失败;它的文档表明如果它将其参数评估为" true"它会成功。虽然在bash [[中是一个内置命令 - 必然如此,因为它需要不同的参数解析规则 - 它仍然是一个命令,它自己评估它的参数,就像[一样。 / p>

2。算术评估的特质

算术评估发生在$(( ... ))(在任何Posix shell中)和许多其他数字上下文(在Bash和其他扩展Posix标准的shell中)的扩展中,包括算术条件{{ 1}}和(( ... ))[[ ... ]]内的数字比较运算符的参数。在bash中,算术计算也用于赋值给赋值为算术的变量(带$[[ ... ]])和下标到数组(不是关联数组)。

出于这个问题的目的,算术评估的最重要特征是参数可以是shell变量的名称(只是名称,没有declare -i)。在这种情况下,如果可能的话,将该变量的值转换为整数,并将其用作参数。尽管Posix标准不要求,但几乎所有shell都会考虑未定义变量的值或值为空的变量。如果该值无法转换为数字,则会产生错误。

这与变量名前面有$的情况略有不同。如果变量名称前面有$,则在计算算术表达式之前,普通参数替换将在正常之前进行。因此,对于问题中的第二个例子,

$

参数扩展的结果将是

VAR=xyz1 && [[ $VAR -eq $VAR ]] && echo Yes || echo No

并且由于[[ xyz1 -eq xyz1 ]] (可能)未定义,因此将评估为将0与0进行比较,这是真的(因此命令将成功)。如果将xyz1定义为数字字符串,则会出现相同的结果,但如果其值无法转换为整数,则会出现相同的结果:

xyz1

Bash的数字评估规则实际上要复杂得多(如果应用于不受信任的输入,甚至是不安全的)。我不会详细介绍所有细节,但基本上bash会对名称在算术评估中用作参数的变量的进行算术评估。实际上,这允许递归替换变量名,但它也允许您将变量的值设置为更复杂的值:

$ VAR=xyz1 && xyz1=42 && [[ $VAR -eq $VAR ]] && echo Yes || echo No
Yes
$ VAR=xyz1 && xyz1=42z && [[ $VAR -eq $VAR ]] && echo Yes || echo No
bash: [[: 42z: value too great for base (error token is "42z")
No