Answers:
原因是-eq
强制对参数进行算术评估。
算术运算符:-eq
,-gt
,-lt
,-ge
,-le
和-ne
一个内[[ ]]
(在ksh中,的zsh和bash)方式自动扩展变量名称在C语言中,不需要为主导$
。
为了确认,我们必须研究bash源代码。该手册未提供直接确认。
在test.c
算术运算符的处理内部属于此函数:
arithcomp (s, t, op, flags)
其中s
和t
都是操作数。操作数将传递给此函数:
l = evalexp (s, &expok);
r = evalexp (t, &expok);
该函数evalexp
在内部定义expr.c
,具有以下标头:
/* expr.c -- arithmetic expression evaluation. */
因此,是的,算术运算符的两面都(直接)属于算术表达式评估。直接地,没有,但是没有。
在实践中,具有:
$ x=3
这两个都失败:
$ [[ x = 4 ]] && echo yes || echo no
no
$ [[ x = 3 ]] && echo yes || echo no
no
正确的x
是,没有被扩展并且x
不等于数字。
然而:
$ [[ x -eq 3 ]] && echo yes || echo no
yes
$ [[ x -eq 4 ]] && echo yes || echo no
no
名为变量的变量x
被扩展(即使没有$)。
[…]
在zsh或bash中,这不会发生(在ksh中)。
这与内部发生的情况相同$((…))
:
$ echo $(( x + 7 ))
10
并且,请理解,这是(非常)递归的(破折号和yash除外):
$ a=b b=c c=d d=e e=f f=3
$ echo "$(( a + 7 ))"
10
而且风险很大:
$ x='a[$(date -u)]'
$ [[ x -eq 3 ]] && echo yes || echo no
bash: Tue Dec 3 23:18:19 UTC 2018: syntax error in expression (error token is "Dec 3 23:18:19 UTC 2018")
语法错误很容易避免:
$ a=3; x='a[$(date -u >/dev/tty; echo 0)]'
$ [[ x -eq 3 ]] && echo yes || echo no
Tue Dec 4 09:02:06 UTC 2018
yes
俗话说:清理您的输入
$ [[ ${x//[^0-9]} -eq 3 ]] && echo yes || echo no
no
😮的结尾
(较旧的)外部/usr/bin/test
变量(不是内建的test
)和仍旧的外部变量以及外部变量expr
都不能仅将表达式扩展为整数(并且显然只能扩展为十进制整数):
$ /usr/bin/test "x" -eq 3
/usr/bin/test: invalid integer ‘x’
$ expr x + 3
expr: non-integer argument
[[
关键字,在读取命令时(而不是在扩展后)会检测到运算符和操作数。因此,[[
可以-eq
比说更明智的方式进行治疗[
。但是我想知道的是:在哪里可以找到有关bash用于解释复合命令的文档?在我看来,这似乎不太明显,而且我显然无法在man
或中找到令人满意的解释info bash
。
test
数值比较的操作数-eq
,-gt
,-lt
,-ge
,-le
和-ne
被作为算术表达式。在一定的限制下,它们仍然必须是单壳单词。
Shell Arithmetic中描述了算术表达式中变量名的行为:
允许使用Shell变量作为操作数;在对表达式求值之前执行参数扩展。在表达式中,也可以使用名称来引用外壳变量,而无需使用参数扩展语法。如果使用名称引用而不使用参数扩展语法,则null或未设置的shell变量的值为0。
并且:
引用变量时,其值作为算术表达式求值
但是我实际上找不到文档的那一部分,据说数字比较采用算术表达式。这不是在描述的条件结构下[[
,也不是在描述猛砸条件表达式。
但是,通过实验,它似乎如上所述工作。
所以,像这样的东西工作:
a=6
[[ a -eq 6 ]] && echo y
[[ 1+2+3 -eq 6 ]] && echo y
[[ "1 + 2 + 3" -eq 6 ]] && echo y
也是如此(评估变量的值):
b='1 + 2 + 3'
[[ b -eq 6 ]] && echo y
但这不是;[[ .. ]]
解析时,它不是一个单独的shell字,因此条件语句中存在语法错误:
[[ 1 + 2 + 3 -eq 6 ]] && echo y
在其他算术上下文中,不需要表达式没有空格。这将打印999
,因为方括号明确地界定了索引中的算术表达式:
a[6]=999; echo ${a[1 + 2 + 3]}
另一方面,=
比较是模式匹配,不涉及算术运算,也不涉及在算术上下文(条件构造)中完成的自动变量扩展:
当使用
==
and!=
运算符时,该运算符右边的字符串将被视为一个模式,并根据以下“模式匹配”中所述的规则进行匹配,就好像启用了extglob shell选项一样。的=
操作者是相同的==
。
因为字符串明显不同,所以这是错误的:
[[ "1 + 2 + 3" = 6 ]]
这样,即使数值相同:
[[ 6 = 06 ]]
在这里,字符串(x
和6
)也进行了比较,它们是不同的:
x=6
[[ x = 6 ]]
但是,这将扩展变量,所以这是正确的:
x=6
[[ $x = 6 ]]
arg1 OP arg2
说args可以是正整数或负整数,我猜这可能暗示它们被视为算术表达式。令人困惑的是,这还意味着它们不能为零。:)
[
,并且它们不是算术表达式。相反,Bash抱怨不完整。
[
是一个外部命令,它无法访问shell变量。通常使用内置命令对其进行优化,但其行为仍然相同。
[
与和一样适用[[
。即使使用[
,对-eq
和朋友的操作数也必须是整数,因此也适用。将“必须为整数”表示“被解释为算术表达式”在两种情况下均不适用。(可能至少部分是由于[
您像您所说的那样像普通命令一样行事。)
是的,您的观察是正确的,变量扩展是在双括号下的表达式上执行的[[ ]]
,因此您无需$
在变量名前加上。
这在bash
手册中有明确说明:
[[表情]]
(...)对[[和]]之间的单词不执行单词拆分和路径名扩展;执行波浪号扩展,参数和变量扩展,算术扩展,命令替换,进程替换和引用删除。
注意,这不是单托架版本的情况下[ ]
,作为[
是不是一个壳关键字(语法),而是一个命令(在bash它是内置的,其他外壳可以使用外部,内衬检验)。
(x=1; [[ $x = 1 ]]; echo $?)
return 0
,(x=1; [[ x = 1 ]]; echo $?)
turns 1
,即x
当我们比较字符串时不执行参数扩展。此行为看起来像是由算术扩展触发的算术评估,即中发生了什么(x=1; echo $((x+1)))
。(关于算术评估,man bash
指出“在表达式中,也可以使用名称引用shell变量,而不使用参数扩展语法。)
-gt
运算符期望数字,所以整个表达式就像在inside中一样被重新求值(())
,另一方面,==
期望字符串,因此触发了模式匹配功能。我没有深入研究源代码,但是听起来很合理。
[
是bash内置的shell。
x=1
紧随其后,您的评估会改变[[ x -gt 2]]
吗?