【问题标题】:if statement and calling function in if using bashif 语句和 if 使用 bash 中的调用函数
【发布时间】:2013-03-27 19:53:01
【问题描述】:

我写了一个函数:

check_log(){
    if [ -f "/usr/apps/appcheck.log" ]
    then
         return 1
    else
         return 0
    fi
}

然后我在“if”条件下调用这个函数:

if [ check_log ];
then
    ........statements....
fi

这行得通吗?我在这里很困惑,因为 bash 在成功时返回 0,在失败时返回 1,但是我的函数返回 1 并且条件正在检查 1/0,它得到 1 并且它应该给出失败,但是在我的 shell 脚本条件通过了。

谁能解释一下这个问题?

【问题讨论】:

  • 如果 [ any number ] 做某事,如果 [ 0 ] 什么都不做,那么 number 呢
  • 在 bash 中,您将为此使用数字上下文 -- 不是 [ ][[ ]] 而是 (( )) -- 像这样:if (( variable )); then ... 只有在 @987654331 时才会评估为 true @ 包含大于 0 的数字。
  • ...顺便说一句,当“使用 bash”时,请确保您的 shebang 是 #!/bin/bash#!/usr/bin/env bash,而不是 #!/bin/sh;如果是后者,则并非所有 bash 功能都会启用,因此此处给出的建议并非都有效。

标签: bash testing if-statement


【解决方案1】:
if [ check_log ];

当您使用方括号时,您正在调用test 命令。它等同于if test check_log,它是if test -n check_log 的简写,这反过来意味着“如果"check_log" 不是空字符串”。它根本不会调用您的 check_log 函数。

改成这样:

if check_log;

顺便说一句,函数可以更简单地写成:

check_log() {
    ! [ -f "/usr/apps/appcheck.log" ]
}

函数的返回值是最后一条命令的退出状态,所以不需要显式的返回语句。

【讨论】:

  • 如果 [ 6 ] 做某事而 [ 0 ] 什么都不做,那么数字呢
  • @JumpOffBox 如果要进行数字测试,则必须使用测试命令的数字运算符之一,例如 if [ 6 -ne 0 ]; then ...。但请注意,您不能使用函数代替这些数字之一;您应该将 shell 函数视为命令(可以成功或失败),而不是数学函数(其主要目的是返回值)。
  • @GordonDavisson 请不要为明确针对 bash 而不是 /bin/sh 的人建议 [ ](( 7 != 0 )) 更具可读性(也更不容易出错;(( ))[[ ]] 一样,抑制字符串拆分和全局扩展,因此即使包含意外数据,也无需使用引号来确保语句按预期解析值)。
  • 所以本质上if check_log;会检查返回值是否为假(0),这意味着“成功”。
  • 我不会将 0 称为“假”。退出代码不是布尔值。最好将它们视为错误代码。成功只有一种方式,但失败的方式有很多,因此我们将成功编码为 0,将失败编码为其他任何值。
【解决方案2】:

正如@john-kugelman 所指出的,一种解决方案(也许是最正确的一种)是使用以下语法:

if check_log;

但另一种解决方案是:

if [[ $(check_log; echo $?) -eq 0 ]];

我个人的偏好是后者,因为它可以促进条件语句之间的一致性。但缺点是因为它依赖于命令替换,它会派生一个子进程(即一个子shell),而第一种方法不会。

可以在此处找到有关该主题的有趣阅读:

When does command substitution spawn more subshells than the same commands in isolation?

更新 (2020-12-01):在大多数情况下,由于效率低下和可能触发错误结果的原因,不建议使用上述替代解决方案,尤其是在被调用函数写入stderr 或 stdout 并且该输出没有被捕获。

更新 (2017-10-20):删除我对第二种方法的偏好,因为这些天我的任务是防止不必要的分叉。第一个解决方案中的语法对于非 shell 编程来说并不那么直观,但它确实更有效。

【讨论】:

  • 为什么你更喜欢第二个?
  • 如果您不再建议此答案中给出的做法,您可能会考虑将其删除。我们有新的问题asked by people trying to apply this advice,如果没有问题会更好。
  • @CharlesDuffy 我认为实际删除其他人可能已添加书签的答案并不是最佳做法。替代解决方案在语法上仍然是正确的,它只是碰巧分叉了一个子外壳进行评估,这可能是可取的,也可能不是可取的。生成子壳是一个轻量级的过程,并且这种模式很普遍。
  • 答案不仅效率低下,而且有问题。如果check_log 写入stdout,它写入的任何内容都会出现在-eq 的左侧,并导致测试失败,无论实际退出状态是什么。 从来没有这种情况是好的做法——即使你想明确地测试$?(比如说,在一个特定的范围内),使用不同的语法仍然更好——比如if check_log; [ "$?" -lt 3 ]; then
  • @CharlesDuffy 有趣。我没有考虑到可能有输出到 stdout 的情况(check_log 不这样做)。重定向输出可以解决这种情况if [[ $(check_log > /dev/null; echo $?) -lt 3 ]]; then,但仍然效率低下并且容易出现您建议的问题。至于 never 一个案例,我认为这有点苛刻,也许 never 在您的用例或许多其他用例中 会更合理。更新答案以将人们引向另一个方向。
猜你喜欢
  • 1970-01-01
  • 2020-03-30
  • 2016-03-11
  • 2016-06-28
  • 1970-01-01
  • 1970-01-01
  • 2020-06-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多