bash [[]]命令中的自动变量扩展


13

在中取消引用变量时bash,必须使用$符号。不过,似乎以下各项工作正常:

x=5
[[ x -gt 2 ]]

有人可以解释吗?

编辑:(更多信息)

我的意思是[[]]命令如何以及为什么取消引用不带$号的变量x。是的,如果x = 1,则该语句的计算结果为false(返回状态1)


2
“工作正常”是什么意思?如果x=1紧随其后,您的评估会改变[[ x -gt 2]]吗?
nohillside

我的意思是:[[]]命令如何以及为什么取消引用不带$号的变量x。是的,如果x = 1,则该语句为假(返回状态1)
访客

Answers:


9

原因是-eq强制对参数进行算术评估。

算术运算符:-eq-gt-lt-ge-le-ne一个内[[ ]](在ksh中,的zsh和bash)方式自动扩展变量名称在C语言中,不需要为主导$

  • 为了确认,我们必须研究bash源代码。该手册未提供直接确认。

    test.c算术运算符的处理内部属于此函数:

    arithcomp (s, t, op, flags)

    其中st都是操作数。操作数将传递给此函数:

    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
fra-san

Bash在我能找到的任何地方都没有对此进行记录。man ksh93中一种描述:还允许进行以下过时的算术比较:exp1 -eq exp2。在man zshbuiltins的这一节中,算术运算符期望使用整数参数而不是算术表达式。这证实了在此引号未指定的条件下,内置测试将某些参数视为算术表达式。我将用源代码确认…。…test
Isaac

7

数值比较的操作数-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 ]] 

在这里,字符串(x6)也进行了比较,它们是不同的:

x=6
[[ x = 6 ]]

但是,这将扩展变量,所以这是正确的:

x=6
[[ $x = 6 ]]

实际上找不到文档中说数字比较采用算术表达式的部分。确认是在代码
艾萨克

最接近的是的描述arg1 OP arg2说args可以是正整数或负整数,我猜这可能暗示它们被视为算术表达式。令人困惑的是,这还意味着它们不能为零。:)
Barmar

@Barmar,恩,对。但这也适用于数字比较[,并且它们不是算术表达式。相反,Bash抱怨不完整。
ilkkachu

@ilkkachu [是一个外部命令,它无法访问shell变量。通常使用内置命令对其进行优化,但其行为仍然相同。
巴尔(Barmar)'18

@Barmar,我的意思是短语“ Arg1和arg2可以是正整数或负整数”。出现在Bash条件表达式中,该列表[与和一样适用[[。即使使用[,对-eq和朋友的操作数也必须是整数,因此也适用。将“必须为整数”表示“被解释为算术表达式”在两种情况下均不适用。(可能至少部分是由于[您像您所说的那样像普通命令一样行事。)
ilkkachu

1

是的,您的观察是正确的,变量扩展是在双括号下的表达式上执行的[[ ]],因此您无需$在变量名前加上。

这在bash手册中有明确说明:

[[表情]]

(...)对[[和]]之间的单词不执行单词拆分和路径名扩展;执行波浪号扩展,参数和变量扩展,算术扩展,命令替换,进程替换和引用删除。

注意,这不是单托架版本的情况下[ ],作为[是不是一个壳关键字(语法),而是一个命令(在bash它是内置的,其他外壳可以使用外部,内衬检验)。


1
感谢您的回覆。看来这仅适用于数字。x = city [[$ x == city]]如果没有$符号,这将不起作用。
来宾

3
看起来这里还有更多:(x=1; [[ $x = 1 ]]; echo $?)return 0(x=1; [[ x = 1 ]]; echo $?)turns 1,即x当我们比较字符串时不执行参数扩展。此行为看起来像是由算术扩展触发的算术评估,即中发生了什么(x=1; echo $((x+1)))。(关于算术评估,man bash指出“在表达式中,也可以使用名称引用shell变量,而不使用参数扩展语法。)
fra-san

@ fra-san确实,因为-gt运算符期望数字,所以整个表达式就像在inside中一样被重新求值(()),另一方面,==期望字符串,因此触发了模式匹配功能。我没有深入研究源代码,但是听起来很合理。
jimmij

[是bash内置的shell。
尼扎姆·穆罕默德

1
@NizamMohamed这是一个内置函数,但仍然不是关键字。
Kusalananda
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.