为什么 Bash `(())` 在 `[[]]` 中不起作用?

Lás*_*gyi 15 linux bash

[[ " stop start status " =~ " $2 " && (($#<3)) ]] || { echo "Usage $0 file_name command"; exit 1;}
Run Code Online (Sandbox Code Playgroud)

我经常使用上述解决方案来检查我的 Bash 脚本的输入范围。

现在我意识到扩展的算术表达式(())看起来像是在双括号内被抑制了[[]]

为了说明问题:

a=start; n=1; [[ " stop start status " =~ " $a " && (($n<3)) ]] && echo ok || echo bad
ok
a=start; n=5; [[ " stop start status " =~ " $a " && (($n<3)) ]] && echo ok || echo bad
bad

# But:
a=start; n=100; [[ " stop start status " =~ " $a " && (($n<3)) ]] && echo ok || echo bad
ok
Run Code Online (Sandbox Code Playgroud)

上面的结果是错误的,因为如果将它们视为数字,则 n 不小于 3。这是正确的解决方案:

a=start; n=100; [[ " stop start status " =~ " $a " ]] && (($n<3)) && echo ok || echo bad
bad
a=start; n=1; [[ " stop start status " =~ " $a " ]] && (($n<3)) && echo ok || echo bad
ok
Run Code Online (Sandbox Code Playgroud)

Ini*_*ian 10

GNU bash 手册页[[..]]解释了操作符运行条件表达式和

根据条件表达式的计算返回01的状态expression。表达式由以下Bash 条件表达式中描述的主要元素组成。

但是算术运算符不是内部支持的条件表达式(primaries)的一部分,[[..]]这意味着该表达式被迫作为字符串比较运行,即

(( $n < 3))
Run Code Online (Sandbox Code Playgroud)

不是在算术上下文中运行,但只是作为普通词典(串)作为比较

[[ 100 < 3 ]] 
Run Code Online (Sandbox Code Playgroud)

这将总是导致真实的,因为ASCII值100才出现3

但是[[..]]如果您使用-lt,则支持内部算术运算,-gt

arg1 OP arg2

OP是一个-eq-ne-lt-le-gt,或-ge。如果分别arg1等于、不等于、小于、小于或等于、大于或大于或等于arg2,则这些算术二元运算符返回 true 。

所以你把你的表达写成

a=start; n=100; [[ " stop start status " =~ " $a " && $n -lt 3 ]] && echo ok || echo bad
bad
Run Code Online (Sandbox Code Playgroud)

它会按预期工作。

或者即使你已经通过在前面,迫使算术表达式使用$((..))和下面写它(注意,庆典不有记载的行为$((..))里面[[..]])。可能的预期行为是在计算 之前扩展算术表达式,[[..]]并且在字符串上下文中计算结果输出,因为[[ 0 ]]这意味着非空字符串。

a=start; n=5; [[ " stop start status " =~ " $a " && $(( $n < 3 )) ]] && echo ok || echo bad
Run Code Online (Sandbox Code Playgroud)

结果看起来仍然很糟糕,因为里面的算术表达式[[..]]分解为一元字符串而不是空的比较表达式

$(( 5 < 3 ))
0
[[ -n 0 ]]
Run Code Online (Sandbox Code Playgroud)

算术评估的结果0(false) 被测试运算符视为非零实体,并在 的右侧断言为真&&。这同样适用于另一种情况,例如说n=1

$(( 1 < 3 ))
1
[[ -n 1 ]]
Run Code Online (Sandbox Code Playgroud)

长话短说,在[[..]].


che*_*ner 7

((是一个引入算术语句的“关键字”。[[但是,Inside不能使用其他语句。不过,您可以使用括号对表达式进行分组,这(( ... ))就是:一个冗余的“双组”。以下都是等价的,由于的优先级<&&

  • [[ " stop start status " =~ " $2 " && (($#<3)) ]]
  • [[ " stop start status " =~ " $2 " && ($#<3) ]]
  • [[ " stop start status " =~ " $2 " && $#<3 ]]

如果您想要整数比较,请使用-lt代替<,但您也不需要将所有内容都放入[[ ... ]]. 您可以在命令列表中同时使用条件语句和算术语句。

{ [[ " stop start status " =~ " $2 " ]] && (($#<3)) ; } || { echo "Usage $0 file_name command"; exit 1;}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,... && ... || ...将按照您期望的方式工作,但通常情况并非如此。更喜欢if声明。

if [[ " stop start status " =~ " $2 " ]] && (($#<3)); then
  echo "Usage $0 file_name command"
  exit 1
fi
Run Code Online (Sandbox Code Playgroud)

  • 这些不是陈述;而是陈述。它们是命令替换,其中“包含”其他命令。要评估命令替换,请执行内部代码并使用它写入标准输出的内容作为替换值。`((...))` 是一个完全不同的结构。同样,“[[ (( 1 &lt; 3 )) &amp;&amp; ... ]]”不使用算术命令;它只是使用括号将 `[[ ... ]]` 内的表达式分组。 (2认同)