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

Gue*_*est 13 bash bash-expansion

取消引用中的变量时bash,必须使用$符号。尽管如此,以下似乎工作得很好:

x=5
[[ x -gt 2 ]]
Run Code Online (Sandbox Code Playgroud)

有人可以解释一下吗?

编辑:(更多信息)

我的意思是 [[ ]] 命令如何以及为什么取消引用我的变量 x 没有 $ 符号。是的,如果 x=1,则该语句被评估为 false(返回状态 1)

ImH*_*ere 9

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

算术运算符:-eq, -gt, -lt, -ge, -leand -neinside a [[ ]](在 ksh、zsh 和 bash 中)意味着像在 c 语言中一样自动扩展变量名,不需要前导$.

  • 为了确认,我们必须查看 bash 源代码。该手册不提供直接确认。

    test.c算术运算符里面的处理落入这个函数:

    arithcomp (s, t, op, flags)
    
    Run Code Online (Sandbox Code Playgroud)

    wherest都是操作数。操作数交给这个函数:

    l = evalexp (s, &expok);
    r = evalexp (t, &expok);
    
    Run Code Online (Sandbox Code Playgroud)

    该函数evalexp在内部定义expr.c,它具有以下标头:

    /* expr.c -- arithmetic expression evaluation. */
    
    Run Code Online (Sandbox Code Playgroud)

    所以,是的,算术运算符的两边都(直接)陷入算术表达式评估。直接,没有但是,没有如果。


在实践中,与:

 $ x=3
Run Code Online (Sandbox Code Playgroud)

这两个都失败了:

 $ [[ x = 4 ]] && echo yes || echo no
 no

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

这是正确的,x没有被扩展并且x不等于一个数字。

然而:

 $ [[ x -eq 3 ]] && echo yes || echo no
 yes

 $ [[ x -eq 4 ]] && echo yes || echo no
 no
Run Code Online (Sandbox Code Playgroud)

命名的变量x被扩展(即使没有 $)。

[…]在 zsh 或 bash中不会发生这种情况(在 ksh 中会发生)。


这与 a 中发生的情况相同$((…))

 $ echo $(( x + 7 ))
 10
Run Code Online (Sandbox Code Playgroud)

并且,请理解这是(非常)递归的(dash 和 yash 除外):

 $ a=b b=c c=d d=e e=f f=3
 $ echo "$(( a + 7 ))" 
 10
Run Code Online (Sandbox Code Playgroud)

一种

而且相当危险:

 $ 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")
Run Code Online (Sandbox Code Playgroud)

可以轻松避免语法错误:

 $ 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
Run Code Online (Sandbox Code Playgroud)

俗话说:净化你的输入

 $ [[ ${x//[^0-9]} -eq 3 ]] && echo yes || echo no
 no
Run Code Online (Sandbox Code Playgroud)

结束


(旧的)外部/usr/bin/test(不是内置的test)和更旧的和外部的expr都不扩展表达式整数(显然,只有十进制整数):

 $ /usr/bin/test "x" -eq 3
 /usr/bin/test: invalid integer ‘x’

 $ expr x + 3
 expr: non-integer argument
Run Code Online (Sandbox Code Playgroud)


ilk*_*chu 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
Run Code Online (Sandbox Code Playgroud)

这也是(评估变量的值):

b='1 + 2 + 3'
[[ b -eq 6 ]] && echo y
Run Code Online (Sandbox Code Playgroud)

但这不是;[[ .. ]]解析时它不是单个 shell 词,因此条件中存在语法错误:

[[ 1 + 2 + 3 -eq 6 ]] && echo y
Run Code Online (Sandbox Code Playgroud)

在其他算术上下文中,表达式不需要没有空格。这将打印999,因为括号明确界定了索引中的算术表达式:

a[6]=999; echo ${a[1 + 2 + 3]}
Run Code Online (Sandbox Code Playgroud)

另一方面,=比较是模式匹配,不涉及算术,也不涉及算术上下文(条件构造)中完成的自动变量扩展:

使用==and!=运算符时,运算符右侧的字符串被视为一个模式,并根据以下模式匹配中描述的规则进行匹配,就好像启用了 extglob shell 选项一样。的=操作者是相同的==

所以这是错误的,因为字符串明显不同:

[[ "1 + 2 + 3" = 6 ]] 
Run Code Online (Sandbox Code Playgroud)

即使数值相同,也是如此:

[[ 6 = 06 ]] 
Run Code Online (Sandbox Code Playgroud)

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

x=6
[[ x = 6 ]]
Run Code Online (Sandbox Code Playgroud)

但是,这会扩展变量,所以这是真的:

x=6
[[ $x = 6 ]]
Run Code Online (Sandbox Code Playgroud)


jim*_*mij 3

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

手册中明确指出了这一点bash

[[ 表达 ]]

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

请注意,这不是单括号版本的情况[ ],因为[它不是 shell 关键字(语法),而是一个命令(在 bash 中它是内置的,其他 shell 可以使用外部的、内联的来测试)。

  • 看起来这里还有更多: `(x=1; [[ $x = 1 ]]; echo $?)` 返回 `0`, `(x=1; [[ x = 1 ]]; echo $?) )` 返回 `1`,即当我们比较字符串时,不会对 `x` 执行参数扩展。这种行为看起来像是由算术扩展触发的算术求值,即“(x=1; echo $((x+1)))”中发生的情况。(关于算术求值,`man bash` 指出“在表达式中,shell 变量也可以通过名称引用,而不使用参数扩展语法)。 (3认同)