【问题标题】:Returning a boolean from a Bash function从 Bash 函数返回布尔值
【发布时间】:2011-07-22 20:39:04
【问题描述】:

我想编写一个 bash 函数来检查文件是否具有某些属性并返回 true 或 false。然后我可以在“if”中的脚本中使用它。但是我应该返回什么?

function myfun(){ ... return 0; else return 1; fi;}

然后我这样使用它:

if myfun filename.txt; then ...

这当然行不通。这如何实现?

【问题讨论】:

  • 删除 function 关键字,myfun() {...} 就足够了
  • if重要的是myfun的零退出状态:如果myfun0退出,则执行then ...;如果是其他任何东西else ... 就会被执行。
  • @nhed:function 关键字是一种 bashism,在其他一些 shell 中会导致语法错误。基本上,它要么是不必要的,要么是禁止的,那么为什么要使用它呢?它甚至不能用作 grep 目标,因为它可能不存在(grep 代表 ())。
  • @GordonDavisson:什么?还有其他贝壳吗? ;-)
  • 请不要使用0和1。见stackoverflow.com/a/43840545/117471

标签: bash shell boolean


【解决方案1】:

0 代表真,1 代表假。

示例:

#!/bin/bash

isdirectory() {
  if [ -d "$1" ]
  then
    # 0 = true
    return 0 
  else
    # 1 = false
    return 1
  fi
}


if isdirectory $1; then echo "is directory"; else echo "nopes"; fi

编辑

从@amichair 的评论来看,这些也是可能的

isdirectory() {
  if [ -d "$1" ]
  then
    true
  else
    false
  fi
}


isdirectory() {
  [ -d "$1" ]
}

【讨论】:

  • 不,您不需要这样做 - 请参阅示例。
  • 为了更好的可读性,您可以使用'true'命令(不执行任何操作并成功完成,即返回0)和'false'命令(不执行任何操作且未成功完成,即返回非零价值)。另外,一个没有显式返回语句结束的函数会返回最后执行的命令的退出代码,所以在上面的例子中,函数体可以缩减为只有[ -d "$1" ]
  • Bengt:当您将其视为“错误代码”时,这是有道理的:错误代码 0 = 一切正常 = 0 错误;错误代码 1 = 这个调用应该做的主要事情失败了;否则:失败!在手册页中查找。
  • 当您考虑到在编程中通常只能以一种方式成功,但可能以无限方式失败时,这是有道理的。好吧,也许不是无限的,但是很多,赔率对我们不利。成功/错误不是布尔值。我认为这是“使用 0 表示真,1 表示假”。应阅读“使用 0 表示成功,使用非零表示失败”。
  • 请不要使用0和1。见stackoverflow.com/a/43840545/117471
【解决方案2】:
myfun(){
    [ -d "$1" ]
}
if myfun "path"; then
    echo yes
fi
# or
myfun "path" && echo yes

【讨论】:

  • 否定呢?
  • 怎么样,@einpoklum?
  • @MarkReed:我的意思是,将“else”案例添加到您的示例中。
  • myfun "路径" ||回声没有
【解决方案3】:

仅使用选项 -d 检查目录时要小心!
如果变量 $1 为空,检查仍然会成功。可以肯定的是,还要检查变量是否为空。

#! /bin/bash

is_directory(){

    if [[ -d $1 ]] && [[ -n $1 ]] ; then
        return 0
    else
        return 1
    fi

}


#Test
if is_directory $1 ; then
    echo "Directory exist"
else
    echo "Directory does not exist!" 
fi

【讨论】:

  • 我不确定这如何回答所提出的问题。虽然很高兴知道空的 $1 可以在空时返回 true,但它并没有提供任何关于如何从 bash 函数返回 true 或 false 的见解。我建议创建一个新问题“对空 shell 变量进行测试时会发生什么?”然后将其发布为答案。
  • 请注意,如果您将正确的引用添加到$1 ("$1"),那么您不需要检查空变量。 [[ -d "$1" ]] 将失败,因为此 "" 不是目录。
【解决方案4】:

如果你重写它可能会起作用 function myfun(){ ... return 0; else return 1; fi;} 就像这个 function myfun(){ ... return; else false; fi;}。也就是说,如果false 是函数中的最后一条指令,则整个函数的结果都是错误的,但return 无论如何都会以真实结果中断函数。我相信至少对我的 bash 解释器来说是这样的。

【讨论】:

    【解决方案5】:

    尽管有 250 多个赞成的答案,你为什么要关心我说的话

    不是0 = true1 = false。它是:零表示没有失败(成功)非零表示失败(N型)

    虽然selected answer 在技术上是“正确的”不要将return 1** 放入您的代码中以表示错误。它会产生一些不幸的副作用。

    1. 经验丰富的开发人员会将您视为业余爱好者(原因如下)。
    2. 经验丰富的开发人员不会这样做(原因如下)。
    3. 容易出错。
      • 即使是经验丰富的开发人员也可能将 0 和 1 分别误认为是 false 和 true(原因如上所述)。
    4. 它需要(或将鼓励)无关紧要和荒谬的 cmets。
    5. 它实际上不如隐式返回状态有用。

    学习一些 bash

    bash manual 说(强调我的)

    返回 [n]

    使 shell 函数停止执行并将值 n 返回给它的调用者。 如果不提供n,则返回值是函数中最后执行的命令的退出状态

    因此,我们不必使用 0 和 1 来表示真假。他们这样做的事实本质上是微不足道的知识,仅对调试代码、面试问题和激发新手的头脑有用。

    bash 手册also says

    否则函数的返回状态是最后执行的命令的退出状态

    bash 手册also says

    ($?) 扩展到最近执行的前台管道的退出状态

    哇,等等。管道?让我们再turn to the bash manual 一次。

    管道是由控制运算符“|”或“|&”之一分隔的一个或多个命令的序列。

    是的。他们说 1 个命令是一个管道。因此,所有这 3 个引用都在说同样的事情。

    • $? 告诉你最后发生的事情。
    • 它会冒泡。

    我的回答

    所以,虽然@Kambus demonstrated 有这么简单的功能,但根本不需要return。我想 与大多数阅读本文的人的需求相比,这简直是不切实际的简单。

    为什么是return

    如果一个函数要返回其最后一个命令的退出状态,为什么还要使用return?因为它会导致函数停止执行。

    多条件下停止执行

    01  function i_should(){
    02      uname="$(uname -a)"
    03
    04      [[ "$uname" =~ Darwin ]] && return
    05
    06      if [[ "$uname" =~ Ubuntu ]]; then
    07          release="$(lsb_release -a)"
    08          [[ "$release" =~ LTS ]]
    09          return
    10      fi
    11
    12      false
    13  }
    14
    15  function do_it(){
    16      echo "Hello, old friend."
    17  }
    18
    19  if i_should; then
    20    do_it
    21  fi
    

    我们这里有...

    04 行是一个显式[-ish] 返回 true,因为 && 的 RHS 只有在 LHS 为 true 时才会执行

    09 行返回与08 行状态匹配的真或假

    13 行返回 false,因为12

    (是的,这可以打高尔夫球,但整个例子都是人为的。)

    另一种常见的模式

    # Instead of doing this...
    some_command
    if [[ $? -eq 1 ]]; then
        echo "some_command failed"
    fi
    
    # Do this...
    some_command
    status=$?
    if ! $(exit $status); then
        echo "some_command failed"
    fi
    

    注意设置status 变量如何揭开$? 的含义。 (当然你知道$? 是什么意思,但是比你知识少的人总有一天会用谷歌搜索它。除非你的代码在进行高频交易,否则show some love,设置变量。)但真正的收获是可以大声读出“如果不存在状态”或相反的“如果退出状态”并解释其含义。 但是,最后一个可能有点过于雄心勃勃,因为看到 exit 这个词可能会让您认为它正在退出脚本,而实际上它正在退出 $(...) 子shell。


    ** 如果你绝对坚持使用return 1 来表示false,我建议你至少使用return 255 来代替。这将导致您未来的自己或任何其他必须维护您的代码的开发人员质疑“为什么是 255?”然后他们至少会注意并有更好的机会避免错误。

    【讨论】:

    • @ZeroPhase 1 & 0 for false & true 会很荒谬。如果您有二进制数据类型,则没有理由这样做。您在 bash 中处理的是反映成功(单数)和失败(复数)的状态代码。它是“if 成功做到这一点,else 做到这一点。”在什么方面取得成功?可能正在检查真/假,可能正在检查字符串、整数、文件、目录、写入权限、glob、正则表达式、grep 或任何其他可能会失败的命令。
    • 避免标准使用 0/1 作为返回值仅仅因为它是钝的并且容易混淆是愚蠢的。 整个 shell 语言是迟钝的并且容易混淆。 Bash 本身在其自己的 truefalse 命令中使用 0/1 = true/false 约定。也就是说,关键字true 的字面意思是 0 状态码。此外,if-then 语句本质上对布尔值进行操作,而不是成功代码。如果在布尔运算期间发生错误,它不应返回 true 或 false,而只是中断执行。否则你会得到误报(双关语)。
    • @Beejor 在你的第一句话中听起来你在说不值得让事情变得更好,因为不可能让它们变得完美。对此我说en.wikipedia.org/wiki/Perfect_is_the_enemy_of_good 对于您的其余声明,听起来您要么没有阅读/理解我的帖子,要么放弃了 bash 并且没有学习它。 “ if-then 语句本质上对布尔值起作用” 呃,笏? “成功代码”笏?听起来你从一个你想要制作的双关语开始,然后向后工作。如果您给了我帮助您的机会,但我错过了,请澄清。
    • " if !$(exit $status); then " -- 这值得更好的解释。这不直观。在打印消息之前,我必须考虑程序是否会退出。您的其余答案都很好,但这破坏了它。
    • @BrunoBronosky 我读了你的回答,我想我理解内容管道(stdin->stdout)和返回值中的错误代码之间的区别。然而,我不明白为什么应该避免使用return 1。假设我有一个validate 函数,如果验证失败,我认为return 1 是合理的。毕竟,如果脚本处理不当(例如,使用set -e 时),这就是停止执行脚本的原因。
    【解决方案6】:

    return 之前使用truefalse 命令,然后使用不带参数的returnreturn 将自动使用您最后一条命令的值。

    return 提供参数不一致,如果您不使用 1 或 0,则为特定类型且容易出错。正如之前的 cmets 所述,此处使用 1 或 0 不是处理此函数的正确方法。

    #!/bin/bash
    
    function test_for_cat {
        if [ $1 = "cat" ];
        then
            true
            return
        else
            false
            return
        fi
    }
    
    for i in cat hat;
    do
        echo "${i}:"
        if test_for_cat "${i}";
        then
            echo "- True"
        else
            echo "- False"
        fi
    done
    

    输出:

    $ bash bash_return.sh
    
    cat:
    - True
    hat:
    - False
    

    【讨论】:

    • 也许可以改进这段代码,这样当你用if test_for_cat 'snow leopard' ; then echo "It's a feline" ; else echo "It's something else" ; fi调用它时它不会吐出奇怪的错误
    【解决方案7】:

    我遇到了一个让我跌跌撞撞的观点(尚未明确提及?)。也就是说,不是如何返回布尔值,而是如何正确评估它!

    我想说if [ myfunc ]; then ...,但这完全是错误的。你不能使用括号! if myfunc; then ... 是这样做的方法。

    正如@Bruno 和其他人重申的那样,truefalse命令,而不是值!这对于理解 shell 脚本中的布尔值非常重要。

    在这篇文章中,我使用布尔变量进行了解释和演示:https://stackoverflow.com/a/55174008/3220983。我强烈建议检查一下,因为它是如此密切相关。

    在这里,我将提供一些从函数返回和评估布尔值的示例:

    这个:

    test(){ false; }                                               
    if test; then echo "it is"; fi                                 
    

    不产生回显输出。 (即false 返回 false)

    test(){ true; }                                                
    if test; then echo "it is"; fi                                 
    

    生产:

    it is                                                        
    

    (即true 返回真)

    test(){ x=1; }                                                
    if test; then echo "it is"; fi                                 
    

    生产:

    it is                                                                           
    

    因为 0(即真)被返回隐式

    现在,这就是把我搞砸的原因......

    test(){ true; }                                                
    if [ test ]; then echo "it is"; fi                             
    

    生产:

    it is                                                                           
    

    test(){ false; }                                                
    if [ test ]; then echo "it is"; fi                             
    

    还产生:

    it is                                                                           
    

    在此处使用括号会产生误报! (我推断“外部”命令结果为0。)

    我的帖子的主要内容是:不要使用方括号来评估布尔函数(或变量),就像你在进行典型的相等检查时那样,例如if [ x -eq 1 ]; then...!

    【讨论】:

      【解决方案8】:

      跟进@Bruno Bronosky 和@mrteatime,我建议您只需“向后”编写布尔返回即可。这就是我的意思:

      foo()
      {
          if [ "$1" == "bar" ]; then
              true; return
          else
              false; return
          fi;
      }
      

      这消除了每个返回语句的丑陋两行要求。

      【讨论】:

      • 漂亮、整洁、乍一看可以理解
      【解决方案9】:

      我发现测试函数输出的最短形式很简单

      do_something() {
          [[ -e $1 ]] # e.g. test file exists
      }
      
      do_something "myfile.txt" || { echo "File doesn't exist!"; exit 1; }
      

      【讨论】:

        【解决方案10】:

        出于代码可读性的原因,我认为返回 true/false 应该:

        • 在一条线上
        • 成为一个命令
        • 容易记住
        • 提及关键字return,后跟另一个关键字(truefalse

        我的解决方案是return $(true)return $(false)如图:

        is_directory()
        {
            if [ -d "${1}" ]; then
                return $(true)
            else
                return $(false)
            fi
        }
        

        【讨论】:

        • 在子shell中执行truefalse会改变外部范围?这是有道理的,考虑到执行失败的二进制文件肯定会触发“失败状态”。您确定以这种方式使用 true 可以“清除”它吗?如果在返回 $(true) 之前执行 false 会发生什么?
        • 我测试了我的担忧,这不是问题。我可以确认此语法按预期工作,至少在 Dash 中是这样。不错!
        • 然而,这样做的缺点是您生成了一个完整的子进程(无论如何在许多解释器中),以便简单地返回一个原语。与任何其他“真实”语言相比,它的效率低下是惊人的!我知道在 shell 脚本中很难避免这种情况,但这是可能的。事实上,我开发了一个库,一般来说,它可以让人们避免这种精神错乱,并且看到它显着加速了一些“重”脚本。虽然我的“向后”答案可读性较差,但如果我关心处理细节,我仍然会使用它而不是这个漂亮的答案。
        • 要挑剔(然后我会放弃它......)这不是“一个命令”顺便说一句。还是两个。一个嵌套在子外壳中。
        • 如果您正在构建一个库并希望满足“重”脚本的需求,那么在某处声明 TRUE=0; FALSE=1return $TRUEreturn $FALSE 将是一个更好的答案。在大多数情况下,对于使用 bash 脚本,代码可读性比性能的轻微提升更重要。如果需要性能和优化,那么 bash 不是答案。
        猜你喜欢
        • 2013-03-11
        • 2017-06-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-05
        • 2012-02-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多