繁体   English   中英

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

[英]Why doesn't Bash `(())` work inside `[[]]`?

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

我经常使用上述解决方案来检查我的 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

上述结果是错误的,因为如果将它们视为数字,则 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

[[..]]的 GNU bash 手册页解释说,运算符运行条件表达式和

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

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

(( $n < 3))

不是在算术上下文中运行,而是像普通的字典(字符串)比较一样

[[ 100 < 3 ]] 

这将始终为真,因为100的 ASCII 值出现在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

它会按预期工作。

或者,即使您通过在((..))之前添加$前缀来强制使用算术表达式并将其编写如下(注意 bash 在[[..]]内没有记录$((..))的行为) . 可能的预期行为是在评估[[..]]之前扩展算术表达式,并且在字符串上下文中将结果 output 评估为[[ 0 ]] ,这意味着非空字符串。

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

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

$(( 5 < 3 ))
0
[[ -n 0 ]]

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

$(( 1 < 3 ))
1
[[ -n 1 ]]

长话短说,在[[..]]中使用正确的操作数进行算术运算。

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

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM