bash中的复合逻辑测试

时间:2012-05-07 17:18:56

标签: bash testing

关于bash的问题......

我写了一些代码来检查一年是否是一个年级。相关部分如下:

if [ $testnum4 -eq 0 -a $testnum100 -ne 0 ] || [ $testnum400 -eq 0 ]
then
  echo 0
fi

嗯,这段代码确实有效。我的问题是我还不太确定我在做什么。只是为了看看会发生什么,我已经尝试了几种变体:将整个测试表达式用方括号括起来,使用&&我有-a-o我{{1}除了我上面写的一个表达式,什么都没有用。我偶然发现它,我很高兴我做到了,但下次我必须做这样的事情,我想知道我在做什么。

所以:任何人都能快速解释复合测试在bash中的运作方式吗?或指向这个主题的好资源的指针?

感谢。

这是失败的代码:

||

4 个答案:

答案 0 :(得分:16)

感兴趣的小方块:方括号实际上是Unix命令。查看系统上是否有名为/bin/[/usr/bin/[的文件。事实上,试试这个:

$ ls i-il /bin[ /bin/test
571541 -r-xr-xr-x  2 root  wheel  43120 Aug 17  2011 /bin/[
571541 -r-xr-xr-x  2 root  wheel  43120 Aug 17  2011 /bin/test

第一个数字是索引节点号。您发现/bin/[/bin/test很难相互关联。他们是同一个计划。

稍后我会回到这个意义......


在BASH和其他Bourne类型的shell中,if命令只运行命令,如果命令返回退出代码0,它将执行if子句中的命令。如果该命令未返回0的退出代码,则会跳过if子句中的命令,并执行else子句中的命令(如果存在)。

因此,这也是有效的:

if sleep 2
then
   echo "That was a good nap!"
fi

您的计算机执行sleep 2命令(如果存在睡眠命令)将返回零退出代码并继续回显这是一个很好的午睡!

这就是所有if命令真的。它运行给定的命令并检查退出代码。


现在,回到[ ...

您可能需要查看test命令的联机帮助页。您可以看到test命令似乎与if语句中的[...]具有许多相同类型的测试。事实上,在最初的Bourne shell(BASH派生自它)中,这些是相同的命令结构:

if test -f $my_file
then
    echo "File $my_file exists"
else
    echo "There is no file $my_file"
fi

if [ -f $my_file ]
then
    echo "File $my_file exists"
else
    echo "There is no file $my_file"
fi

事实上,在最初的Bourne shell中,你必须使用test命令而不是方括号。


所以,回到你原来的问题。 -a-o是测试命令的参数(请参阅test联机帮助页。例如,您的if语句可能已写为:

if test $testnum4 -eq 0 -a $testnum100 -ne 0 || test $testnum400 -eq 0
then
   echo 0
fi

但是,&&||不是测试命令参数,因此不能在测试命令中使用它们。

请注意,[是BASH中的内置命令,因此BASH不会像原始Bourne shell那样执行/bin/[。但是,它仍然是一个命令,就像echo是BASH中的内置命令一样,尽管/bin/echo仍然存在。

答案 1 :(得分:3)

在Bash中进行整数比较的正确方法是使用双括号:

if (( (testnum4 == 0 && testnum100 != 0) || testnum400 == 0 ))

答案 2 :(得分:1)

算法

检查闰年的算法是(伪代码):

if (YEAR modulo 400):
    LEAP_YEAR = true
elseif (YEAR modulo 4) and not (YEAR modulo 100):
    LEAP_YEAR = true
else
    LEAP_YEAR = false
endif

我建议不要使用过于聪明的解决方案,并坚持使用更具可读性的东西。有关使用嵌套if语句的闰年测试的工作示例,请参阅Bash Beginner's Guide。它使用已弃用的$[]算术表达式,因此只需将算术表达式替换为$(( )),您就可以看到它应该如何工作。

OPs代码如何工作

在OP更新的代码示例中,值是从位置参数构建的单独变量。因此,在条件语句中计算变量之前,对变量执行模运算。之后,if语句说:

if (first_expression returns true) or (second_expression returns true):
    echo 0
endif

单方括号只是一个内置的shell,它执行与test命令相同的操作,该命令用于计算表达式并返回退出状态,其中零是真值。有关详细信息,请参阅help \[man test

构建更好的捕鼠器

虽然我和其他海报已经解释了为什么OP代码有效,但似乎OP的部分目标是将整个闰年测试放在一个条件中。这是一个Bash shell函数,可以执行此操作,并让您通过检查退出状态来测试结果。

# function returns exit status of arithmetic expression
leap_year () { 
    (( $1 % 400 == 0 || ($1 % 4 == 0 && $1 % 100 != 0) ))
}

您可以在提示符下测试该功能:

$ leap_year 2011; echo $?
1
$ leap_year 2012; echo $?
0
$ leap_year 2013; echo $?
1

有关(( ))算术表达式的更多信息,请参阅Bash参考手册的Conditional Constructs部分。

答案 3 :(得分:0)

在bash中(实际上是大多数shell)if接受命令/命令集并在最后一个命令返回true(零退出代码)时评估then部分,否则它会计算{{1 } / elif部分(else的处理方式与其他方式类似)。

现在关于elif。这类似于[[命令,但测试覆盖范围更广。但是它的局限性在于它一次只能评估一个条件,因此接受 -o -a 。您可能希望将test视为[的真正别名。对于test||,如果前一个命令分别返回false(非零退出代码)或true(零退出代码),则这些控制操作符将评估下一个命令。这些2的使用是将命令链接在一起(尤其是&&)。

对于上面的if,它执行2个命令:

  • 首先:[[
  • 第二名:[ $testnum4 -eq 0 -a $testnum100 -ne 0 ]

如果第一个命令成功,则if执行[ $testnum400 -eq 0 ]部分。如果第一个命令失败,则执行第二个命令。如果第二个成功,则执行正文,否则then不执行任何操作。

对于好的bash资源,您可能想访问The Bash Wiki