简体   繁体   English

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

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

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

I frequently use the above solution to check the input range of my Bash script.我经常使用上述解决方案来检查我的 Bash 脚本的输入范围。

Now I realise that the extended arithmetic expression (()) looks like it is suppressed inside the double bracket [[]] .现在我意识到扩展的算术表达式(())看起来像是被抑制在双括号[[]]内。

To illustrate the problem:为了说明问题:

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

The above result is false because n not less than 3 if they are treated as numbers.上述结果是错误的,因为如果将它们视为数字,则 n 不小于 3。 This is the correct solution:这是正确的解决方案:

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

The GNU bash man page for [[..]] explains that the operator runs a conditional expression and [[..]]的 GNU bash 手册页解释说,运算符运行条件表达式和

Return a status of 0 or 1 depending on the evaluation of the conditional expression expression .根据条件表达式expression的计算返回状态01 Expressions are composed of the primaries described below in Bash Conditional Expressions .表达式由下面Bash 条件表达式中描述的基元组成。

But the arithmetic operator is not part of the supported conditional expressions ( primaries ) inside [[..]] which means the expression is forced to run as a string comparison, ie但是算术运算符不是[[..]]内支持的条件表达式( primaries )的一部分,这意味着表达式被迫作为字符串比较运行,即

(( $n < 3))

is not run in arithmetic context but just as plain lexicographic (string) comparison as不是在算术上下文中运行,而是像普通的字典(字符串)比较一样

[[ 100 < 3 ]] 

which will always result true, because the ASCII values for 1 , 0 , 0 appear before 3这将始终为真,因为100的 ASCII 值出现在3之前

But inside [[..]] arithmetic operations are supported if you use -lt , -gt但是如果使用-lt-gt ,则支持[[..]]内部算术运算

arg1 OP arg2

OP is one of -eq , -ne , -lt , -le , -gt , or -ge . OP 是-eq-ne-lt-le-gt-ge之一。 These arithmetic binary operators return true if arg1 is equal to, not equal to, less than, less than or equal to, greater than, or greater than or equal to arg2 , respectively.如果arg1分别等于、不等于、小于、小于或等于、大于或大于或等于arg2 ,则这些算术二元运算符返回 true。

So had you written your expression as所以你有没有把你的表达写成

a=start; n=100; [[ " stop start status " =~ " $a " && $n -lt 3 ]] && echo ok || echo bad
bad

it would have worked as expected.它会按预期工作。

Or even if you had forced the arithmetic expression usage by prefixing $ before ((..)) and written it as below (note that bash does not have documented behavior for $((..)) inside [[..]] ).或者,即使您通过在((..))之前添加$前缀来强制使用算术表达式并将其编写如下(注意 bash 在[[..]]内没有记录$((..))的行为) . The likely expected behavior is the arithmetic expression is expanded before the [[..]] is evaluated and the resultant output is evaluated in a string context as [[ 0 ]] which means a non-empty string.可能的预期行为是在评估[[..]]之前扩展算术表达式,并且在字符串上下文中将结果 output 评估为[[ 0 ]] ,这意味着非空字符串。

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

The result would still look bad, because the arithmetic expression inside [[..]] decomposes into an unary string not empty comparison expression as结果看起来仍然很糟糕,因为[[..]]内的算术表达式分解为一元字符串而不是空比较表达式

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

The result of the arithmetic evaluation 0 (false) is taken as a non-zero entity by the test operator and asserts true on the right-side of && .算术评估的结果0 (false) 被测试运算符视为非零实体,并在&&的右侧断言 true。 The same would apply for the other case also eg say n=1这同样适用于其他情况,例如说n=1

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

So long story short, use the right operands for arithmetic operation inside [[..]] .长话短说,在[[..]]中使用正确的操作数进行算术运算。

(( is a "keyword" that introduces the arithmetic statement. Inside [[ , however, you can't use other statements. You can use parentheses to group expressions though, so that's what ((... )) is: a redundant "double group". The following are all equivalent, due to the precedences of < and && : ((是引入算术语句的“关键字”。但是,在[[内部,您不能使用其他语句。但是,您可以使用括号对表达式进行分组,这就是((... ))的含义:冗余“双组”。由于<&&的优先级,以下都是等价的:

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

If you want integer comparison, use -lt instead of < , but you also don't need to fit everything inside [[... ]] .如果您想要 integer 比较,请使用-lt而不是< ,但您也不需要将所有内容都放入[[... ]] You can use a conditional statement and an arithmetic statement together in a command list.您可以在命令列表中同时使用条件语句和算术语句。

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

In this case, ... &&... ||... will work the way you expect, though in general that is not the case.在这种情况下, ... &&... ||...将按照您期望的方式工作,但通常情况并非如此。 Prefer an if statement instead.更喜欢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