我最近写了一个相当复杂的bash脚本,在测试期间我注意到了一个错误行为。在分析过程中,我发现有一个错误,在条件命令中我意外地使用了-eq而不是==,也就是说,我有一个类似的
[[ "$var1" -eq "$var2" ]] || { ... }而不是
[[ "$var1" == "$var2" ]] || { ... }因为在本例中,var1和var2可能包含任意字符串,而不仅仅是整数,所以在那个地方发生了可怕的错误。在命令行进行快速调查使问题更加清楚:
root@cerberus:~/scripts# [[ '0' -eq '0' ]] && echo Equal
Equal
root@cerberus:~/scripts# [[ '0' -eq '1' ]] && echo Equal
root@cerberus:~/scripts# [[ 'A' -eq 'B' ]] && echo Equal
Equal最后一行对我来说很令人惊讶。从巴什手册 (在页面的末尾,强调我的意思):
arg1 OP arg2 OP是‘-eq’、‘-ne’、‘- is’、‘-le’、‘-gt’或‘-ge’之一。如果arg1分别等于、不等于、小于、小于或等于、大于或等于arg2,则这些算术二进制运算符返回true。Arg1和arg2可以是正整数,也可以是负整数。与[[命令、Arg1和Arg2 ]一起使用时被计算为算术表达式(参见Shell算术).
由于上面的代码中的最后一行使用-eq和[[命令,因此根据上面的引文将'A'和'B'计算为算术表达式。令我惊讶的是,这并没有造成bash抛出错误,因为显然没有合理的方法将'A'或'B'转换为整数。
然后,我试图找出bash在计算算术表达式时应用的规则。手册中的各章描述了一些边缘情况(例如表达式的展开是null或unset),但是对于一个表达式的展开是一个不能算术计算的任意字符串的情况,它没有任何规定。注意:我知道我们不应该使用仅链接的引用,但是相应的部分太长了,不能被引用。
出于好奇,我试图找出像'A'这样的算术值的实际计算值。这比预期的要困难得多,因为bash实际上拒绝对字符串进行“正常”算术扩展,即使可以对这些字符串进行算术计算:
root@cerberus:~/scripts# echo $(( 0 ))
0
root@cerberus:~/scripts# echo $(( '0' ))
-bash: '0' : syntax error: operand expected (error token is "'0' ")
root@cerberus:~/scripts# echo $(( 'A' ))
-bash: 'A' : syntax error: operand expected (error token is "'A' ")由于我想要解决这个问题,而不是症状,所以我放弃了尝试找出bash在算术计算任意字符串时应用的规则,或者为什么它在算术扩展时抛出错误,而不是在与[[一起进行算术计算时抛出错误。
相反,我现在想知道bash中是否有一个选项使它在中抛出一个错误--在任何情况下,它必须对表达式进行算术计算,但这是不可能的,因为该表达式不是算术表达式。如果没有这样的选项,我想知道,在-eq、-ne (等等)的参数在[[ ]]中使用时,这些参数的算术计算是否有可能做到这一点。
发布于 2020-11-19 13:39:48
-eq在[[ ... ]]中将其操作数计算为算术表达式,即它们在((...))中的行为类似。有或没有美元符号的变量都会展开,但如果它们的展开是有效的标识符,则该名称的变量用于生成要比较的值。
#!/bin/bash
A=foo
B=bar
for foo in 0 1 ; do
for bar in 0 1 ; do
if [[ A -eq B ]] ; then
echo "$A ($foo) eq $B ($bar)"
else
echo "$A ($foo) ne $B ($bar)"
fi
done
done这种扩展是递归发生的。
#!/bin/bash
a=1 b=a c=b d=c e=d f=e g=f h=g i=h j=i k=j l=k m=l
n=m o=n p=o q=p r=q s=r t=s u=t v=u w=v x=w y=x z=y
[[ z -eq 1 ]] && echo ok递归限制为1024:
#!/bin/bash
(
echo -n a=
for var in {a..z}{a..z}{a..z} ; do
echo $var
echo -n $var=
done
echo 42
echo '[[ a -eq 42 ]] && echo ok'
) | bash发布于 2020-11-19 13:15:13
如果您使用[ ... ]而不是[[ ... ]],我认为您将得到您想要的行为。鉴于以下职能:
check_equal() {
if [ "$var1" -eq "$var2" ]; then
echo equal
else
echo not equal
fi
}这些工作如预期的那样:
$ var1=0 var2=0 check_equal
equal
$ var1=0 var2=1 check_equal
not equal但是,传递非数值参数会导致错误消息:
$ var1=A var2=0 check_equal
testnumbers.sh: line 2: [: A: integer expression expected
not equal当然,这里的缺点是您使用的是[ ... ]表达式,它在某些方面比bash专用的[[ ... ]]表达式更脆弱。
https://stackoverflow.com/questions/64909825
复制相似问题