首页 > 解决方案 > 双括号构造如何在 bash 中工作?

问题描述

我在这里读到双括号允许 C 风格的变量操作。但是,当我尝试比较字符串时,它没有按预期工作:

(( "a" == "b" )) && echo yes || echo no
# shows yes

我也很困惑如何变量用作布尔值。按照链接答案中的示例,我尝试了以下操作:

true=1
false=0
a=true
(( a )) && echo yes || echo no
# shows yes
a=false
(( a )) && echo yes || echo no
# shows no

但不会a是真假的字符串值吗?

此外,由于在 bash 中将“无错误”值 (0) 视为 true,并且将任何“错误”值(非零)视为 false,为什么看起来这里采用了相反的约定?

标签: bashsyntaxparentheses

解决方案


需要注意的主要事情是双括号结构允许算术评估和扩展,并且不是内联 C 解释器。因此,只有 Shell Arithmetic中定义的规则适用,也就是说,只有整数类型的 C 运算在双括号中有效。


第一个例子:bash 构造首先展开

算术运算符之外的任何内容首先根据 bash 规则进行扩展,例如引号参数扩展、bash 范围{1..5}和列表{a,b}结构,然后再开始计算双括号。

在第一个示例中,双引号导致里面的内容被解释为一个单词(在双括号内无效)并且还评估以 $ 开头的内容(但引号内没有),所以第一个示例简单地变成了(( a == b )).

因此,了解如何(( ))工作的最佳方法是首先在您的脑海中计算出所有 bash 结构,然后将其插入。您还可以编写示例来测试您的假设。

发生参数扩展的示例:

a=1
b=2
(( a = $b )) # becomes (( a = 2 ))
(( a = b )) # straight arithmetic evaluation of a = b within the double parenthesis
# they produce the same result but how they arrive at the result is different
(( $a = b )) # becomes (( 2 = b ))
# syntax error as that's not a valid expression.

笔记

$(( ))当您比较密切相关的和(( ))构造时,会有一些特殊性。前者(算术扩展)将表达式视为在双引号内,而后者则不是,如上所述。


第二个例子:右值位置的变量递归扩展

Shell Arithmetic中有一些微妙的规则:

  1. “变量的值在被引用时被计算为算术表达式”
  2. “或者当一个已经被赋予整数属性的变量declare -i被赋值时”。
  3. “在不使用参数扩展语法的情况下按名称引用时,为 null 或未设置的 shell 变量的计算结果为 0”

在尝试了一下之后,您会发现这基本上意味着 rvalue 中的任何变量都将被递归评估,直到它达到一个整数或未定义/空变量名的值:

b=c
c=d
(( a = b ))
echo $a 
# gives 0
d=3
(( a = b ))
echo $a
# gives 3

unset d
declare -i a
a=b
echo $a
# gives 0
d=3
a=b
echo $a
# gives 3

您还可以通过将表达式放入变量中并稍后对其进行评估来玩技巧:

b=2
c=3
d=c
e=b+d
(( a = e ))
echo $a
# gives 5, as it unfolds like a=b+d; a=2+c; a=2+3

因此,在问题的示例中,a评估为true,然后评估为 1 以给出最终结果。


如何(( ))反转真假的解释

(( 0 ))
echo $? # print the return code of the previous call
# prints 1, which indicates error/false in shell

(( 1 ))
echo $?
# prints 0, indicating success/true

(( 2 ))
echo $?
# prints 0

(( -1 ))
echo $?
# prints 0

所以括号内的行为与 C 对真假的解释是一致的,0 表示假,非零表示真。(( ))将 false“转换”为返回值 1,将 true 转换为返回值 0。


推荐阅读