6

关于 bash 的问题....

我写了一个小代码来检查一年是否是闰年。相关部分如下所示:

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

好吧,这段代码确实有效。我的问题是我仍然不太确定自己在做什么。只是为了看看会发生什么,我尝试了几种变体:将整个测试表达式括在方括号中,使用&&where I have-a-owhere I have||等。除了我上面写的一个表达式之外,没有任何效果。我偶然发现了它,我很高兴我做到了,但是下次我必须做这样的事情时,我想知道我在做什么。

所以:任何人都可以快速解释复合测试在 bash 中的工作原理吗?或指向有关此主题的良好资源的指针?

谢谢。

这是一个失败的代码:

let testnum4=$1%4
let testnum100=$1%100
let testnum400=$1%400

if [ $testnum4 -eq 0 && $testnum100 -ne 0 ] -o [ $testnum400 -eq 0 ]
then
  echo 0
fi
4

4 回答 4

18

小注意:方括号实际上是一个 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

第一个数字是 i 节点编号。你看到了,/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命令(如果存在 sleep 命令)将返回零退出代码并继续回显 这是一个很好的午睡!.

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


现在,回到[...

您可能需要查看测试命令的联机帮助页。您可以看到该命令似乎有许多与 if 语句中test相同类型的测试。[...]事实上,在最初的 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 不会/bin/[像原来的 Bourne shell 那样执行。然而,它仍然是一个命令,很像echoBASH 中的内置命令,尽管/bin/echo仍然存在。

于 2012-05-07T21:44:59.280 回答
4

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

if (( (testnum4 == 0 && testnum100 != 0) || testnum400 == 0 ))
于 2012-05-07T19:44:23.277 回答
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 初学者指南。它使用不推荐使用的$[]算术表达式,因此只需将算术表达式替换为$(( )),您就可以看到它应该如何工作。

OPs 代码是如何工作的

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

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

单方括号只是一个 shell 内置函数,其执行与test命令相同,它计算表达式并返回退出状态,其中零为真。请参阅help \[man test了解更多信息。

建造一个更好的捕鼠器

虽然我和其他发帖人已经解释了为什么 OPs 代码有效,但似乎 OPs 目标的一部分是将整个闰年测试放在一个条件中。这是一个 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 参考手册的条件构造部分。

于 2012-05-07T18:10:14.587 回答
0

在 bash(实际上大多数 shell)中,如果最后一个命令返回 true(零退出代码),则if接受一个命令/一组命令并评估该then部分,否则它评估elif/else部分(elif以类似于 else 的方式处理)。

现在关于[[. 这类似于test命令,但具有更大的测试覆盖率。但是它有一个限制,它一次只能评估一个条件,所以它接受-o-a。您可能希望将其[视为test. 至于||&&这些是控制运算符,如果上一个命令分别返回 false(非零退出代码)或 true(零退出代码),则评估下一个命令。这 2 个的用途是将命令链接在一起(尤其是[[)。

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

  • 第一的:[ $testnum4 -eq 0 -a $testnum100 -ne 0 ]
  • 第二:[ $testnum400 -eq 0 ]

如果第一个命令成功,则 if 执行该then部分。如果第一个命令失败,则执行第二个命令。如果第二个成功,则执行主体,否则if不执行任何操作。

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

于 2012-05-07T17:32:47.670 回答