[英]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
的计算返回状态0
或1
。 表达式由下面Bash 条件表达式中描述的基元组成。
但是算术运算符不是[[..]]
内支持的条件表达式( primaries )的一部分,这意味着表达式被迫作为字符串比较运行,即
(( $n < 3))
不是在算术上下文中运行,而是像普通的字典(字符串)比较一样
[[ 100 < 3 ]]
这将始终为真,因为1
、 0
、 0
的 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.