【问题标题】:Difference between return and exit in Bash functionsBash函数中返回和退出的区别
【发布时间】:2011-05-24 03:02:43
【问题描述】:

关于退出代码,Bash 函数中的returnexit 语句有什么区别?

【问题讨论】:

  • 专业提示:在您的 shell 中键入 help <command> 以获取有关 shell 内置功能的信息。在你的情况下help returnhelp exit
  • Protip #2:在你的 shell 中输入 type <command> 以获取有关它是否是内置 Bash 的信息。
  • 如果你想退出脚本,无论是否有源,你可以这样做:return 2> /dev/null | exit。它会先尝试返回,如果不能返回,它不会显示任何错误并会使用退出。
  • Protip #4:man exit

标签: bash function return-value return exit


【解决方案1】:

来自man bashreturn [n]

使函数停止执行并将 n 指定的值返回给它的调用者。如果省略 n,则返回状态为函数体中最后执行的命令的状态。

...exit [n]:

使 shell 以 n 状态退出。如果省略 n,则退出状态为最后执行的命令的状态。在 shell 终止之前执行 EXIT 陷阱。

编辑:

根据您对问题的编辑,关于退出代码,return 与退出代码无关。退出代码用于应用程序/脚本,而不是函数。所以在这方面,唯一设置脚本退出代码的关键字(可以被调用程序使用$? shell 变量捕获的关键字)是exit

编辑 2:

我最后一个提到exit 的声明引起了一些cmets。为了理解 OP,它区分了 returnexit,事实上,在程序/shell 脚本的任何给定点,exit 是唯一的结束方式带有调用进程退出代码的脚本。

在 shell 中执行的每个命令都会产生一个本地“退出代码”:它将$? 变量设置为该代码,并且可以与if&& 和其他运算符一起使用以有条件地执行其他命令。

这些退出代码(以及$? 变量的值)会在每次命令执行时重置。

顺便说一句,脚本执行的最后一个命令的退出代码被用作调用进程所看到的脚本本身的退出代码。

最后,函数在被调用时,作为退出代码的 shell 命令。函数的退出代码(在函数内)使用return 设置。因此,当函数return 0 运行时,函数执行终止,退出代码为 0。

【讨论】:

  • 不完全是。它总是从当前 shell 返回一个值。不管你是否在函数内部。
  • 评论您的编辑:我可能混淆了返回值和退出代码,但func(){ return 50; };func;echo $? 回显 50。所以$? shell 变量似乎不限于exit
  • "$? 扩展到最近执行的前台管道的退出状态。"该退出可能以调用exit(或到达脚本末尾)的形式或以调用函数内的return 的形式从shell 退出。
  • @lecodesportif:当前进程/脚本的$? 仅限于exit 或此脚本执行的最后一个命令的结果。因此,如果您的最后一个脚本行是对该函数的调用,并且该函数返回 50,是的,您为 调用您的进程生成的 $? 是 50。但是,那不是与return 无关,因为这仅限于当前脚本。仅当此函数调用是脚本的最后一句时,它才会返回。但是,exit 始终完成脚本并将该值作为 $?返回给调用进程
  • -1 让我对“return 与退出代码无关”这一行感到困惑。实验告诉我,函数的返回码和脚本的退出码在功能上没有区别。
【解决方案2】:

return 将导致当前函数超出范围,而exit 将导致脚本在调用它的位置结束。下面是一个示例程序来帮助解释这一点:

#!/bin/bash

retfunc()
{
    echo "this is retfunc()"
    return 1
}

exitfunc()
{
    echo "this is exitfunc()"
    exit 1
}

retfunc
echo "We are still here"
exitfunc
echo "We will never see this"

输出

$ ./test.sh
this is retfunc()
We are still here
this is exitfunc()

【讨论】:

  • 很好的例子。您还可以在$? 中显示退出值 1。
  • 请注意,如果您在调用“retfunc”之前添加“set -e”,此函数将不会打印“We are still here”。
  • 但是,echo fnord | while read x; do exitfunc; done; echo "still here" 将打印“仍然在这里”。在这种情况下似乎只退出了while 子shell。
  • +1 添加:```return 会导致当前函数或源脚本超出范围```。
  • 请注意,在上面的示例中,如果您使用set -e 运行,脚本在第一个错误时退出,它将在第一个函数调用返回非零值后退出。
【解决方案3】:

我认为没有人真正完全回答了这个问题,因为他们没有描述如何使用这两者。好的,我想我们知道 exit 会杀死脚本,无论它被调用到哪里,你也可以为它分配一个状态,例如 exit 或 exit 0 或 exit 7 等等。这可用于确定脚本在被另一个脚本调用等情况下如何强制停止。exit 就足够了。

return,当被调用时,将返回指定的值以指示函数的行为,通常为 1 或 0。例如:

    #!/bin/bash
    isdirectory() {
      if [ -d "$1" ]
      then
        return 0
      else
        return 1
      fi
    echo "you will not see anything after the return like this text"
    }

像这样检查:

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

或者像这样:

    isdirectory || echo "not a directory"

在此示例中,测试可用于指示是否找到了目录。请注意,return 之后的任何内容都不会在函数中执行。 0 为真,而 shell 中的假为 1,与其他编程语言不同。

更多函数信息:Returning Values from Bash Functions

注意isdirectory 函数仅用于教学目的。这不应该是您在真实脚本中执行此类选项的方式。*

【讨论】:

  • 或者只是使用test -d $1 来达到同样的效果。永远不要if <check> return else return<check> 至少在我知道的所有语言中都会做同样的事情。
  • 为了更明确地了解 erik 的意思:isdirectory() { [ -d "$1" ]; } 的行为与您在此处的行为完全相同:shell 函数的默认返回值,无论是到达其代码的末尾或者通过不带参数的return,是最近的命令。
  • 这里的其他评论者正在批评 Mike Q 示例的风格,而他实际上是在谈论 returnstatement 的行为。确实,他的示例过于简单,不能用于生产。但这很简单,所以它确实很好地完成了他的任务。没有错。
  • 谢谢 Mike S,是的,我同意最简单的例子最能解释退出与返回。其他 cmets 肯定是有效的,应该考虑用于更高级的 bash 编码器;-)
  • @erikbwork 这是大多数学习材料中的常见做法。作为妥协,我根据您的意见在帖子中添加了免责声明。
【解决方案4】:

请记住,函数是脚本的内部函数,通常使用 return 语句从调用它们的位置返回。调用外部脚本完全是另一回事,脚本通常以退出语句终止。

“关于退出代码的 Bash 函数中的 return 和 exit 语句之间的差异”非常小。两者都返回一个状态,而不是 values 本身。状态为零表示成功,而任何其他状态(1 到 255)表示失败。 return 语句将返回到调用它的脚本,而 exit 语句将从遇到它的任何地方结束整个脚本。

return 0  # Returns to where the function was called.  $? contains 0 (success).

return 1  # Returns to where the function was called.  $? contains 1 (failure).

exit 0  # Exits the script completely.  $? contains 0 (success).

exit 1  # Exits the script completely.  $? contains 1 (failure).

如果您的函数只是在没有 return 语句的情况下结束,则最后执行的命令的状态将作为状态码返回(并将放在 $? 中)。

记住,返回和退出返回一个从 0 到 255 的状态码,在 $? 中可用。您不能将任何其他内容填充到状态代码中(例如,return "cat");不起作用。但是,脚本可以通过使用状态码回传 255 种不同的失败原因。

您可以设置调用脚本中包含的变量,或者echo导致函数并在调用脚本中使用命令替换;但返回和退出的目的是传递状态码,而不是像 C 这样的编程语言中所期望的值或计算结果。

【讨论】:

    【解决方案5】:

    有时,您使用.source 运行脚本。

    . a.sh
    

    如果您在a.sh 中包含exit,它不仅会终止脚本,还会结束您的shell 会话。

    如果您在a.sh 中包含return,它只会停止处理脚本。

    【讨论】:

    • 但是当我只运行 a.sh 时,我得到一个错误return: can only 'return' from a function or sourced script,这使得它不适合一般的脚本。
    • 在脚本的顶层,两者都不适合all 的情况。使用 .source 在当前 shell 中运行脚本,而不是生成子 shell。脚本必须知道如何使用它。对相反的用户有祸了。就个人而言,我建议在第一次运行之前阅读脚本。
    • 我遇到的一个很棒的技巧是对ERR EXIT 使用trap 函数,然后首先保存失败命令errCode=$? 的退出代码,然后退出脚本(有源或无源)使用return $errCode || exit $errCode,其中|| 的意思是“如果我因为没有来源而无法返回,那就退出吧”。
    【解决方案6】:

    简单来说(主要针对编码新手),我们可以说,

        `return`: exits the function,
        `exit()`: exits the program (called as process while running)
    

    另外如果你观察到了,这是非常基本的,但是......,

        `return`: is the keyword
        `exit()`: is the function
    

    【讨论】:

    • 在 bash 脚本中,exitreturn 差不多是一个函数。它们是内置命令。它们甚至不是保留字。
    【解决方案7】:
    • exit 终止当前的进程;无论有没有退出代码,都将其视为系统而不是程序功能。请注意,当sourcing 时,exit 将结束 shell。但是,在运行时,它只会exit 脚本。

    • return 从函数返回到调用后的指令,带或不带返回码。 return 是可选的,它隐含在函数的末尾。 return 只能在函数内部使用。

    我想补充一点,在获取源代码的同时,要从函数中 exit 脚本而不杀死 shell 并不容易。我认为,“测试”脚本上的示例更好:

    #!/bin/bash
    function die(){
       echo ${1:=Something terrible wrong happen}
       #... clean your trash
       exit 1
    }
    
    [ -f /whatever/ ] || die "whatever is not available"
    # Now we can proceed
    echo "continue"
    

    执行以下操作:

    user$ ./test
    Whatever is not available
    user$
    

    test -并且- shell 将关闭。

    user$ . ./test
    Whatever is not available
    

    只有test 会完成并显示提示。

    解决方案是将潜在过程包含在() 中:

    #!/bin/bash
    function die(){
       echo $(1:=Something terrible wrong happen)
       #... Clean your trash
       exit 1
    }
    
    ( # Added        
        [ -f /whatever/ ] || die "whatever is not available"
        # Now we can proceed
        echo "continue"
    ) # Added
    

    现在,在这两种情况下,只有test 会退出。

    【讨论】:

    • 添加 () 将该块放入子 shell 中,有效地取消执行 .(源)命令,就像您正常运行测试脚本一样,即在一个子外壳中。 IOf 脚本未使用 .source 运行,那么您实际上有 2 个子 shell。
    【解决方案8】:

    OP的问题: 关于退出代码,BASH 函数中的 return 和 exit 语句有什么区别?

    首先,需要澄清一下:

    • (return|exit) 语句不需要终止 (function|shell) 的执行。 (function|shell) 将在到达其代码列表的末尾时终止,即使没有 (return|exit) 语句。

    • (return|exit) 语句不需要从终止的 (function|shell) 传回值。每个进程都有一个内置变量$?,它总是有一个数值。它是一个特殊变量,不能像“?=1”那样设置,但只能以特殊方式设置(见下文*)。

      $ 的价值?在(被调用函数|子shell)中执行的最后一个命令之后是传递回(函数调用者|父shell)的值。无论执行的最后一个命令是 ("return [n]"| "exit [n]") 还是普通的 ("return" 或其他恰好是被调用函数代码中的最后一个命令的东西,都是如此。

    在上面的项目符号列表中,从“(x|y)”中选择始终是第一项或始终是第二项,以分别获取有关函数和返回或 shell 和退出的语句。

    很明显,它们都共享特殊变量$? 的共同用法,以便在它们终止后向上传递值。

    * 下面是可以设置$? 的特殊方式:

    • 当被调用函数终止并返回给调用者时,$?调用者中的值将等于终止函数中$? 的最终值。
    • 当父 shell 隐式或显式等待单个子 shell 并通过终止该子 shell 释放时,父 shell 中的 $? 将等于终止子 shell 中 $? 的最终值.
    • 一些内置函数可以根据其结果修改$?。但有些则不然。
    • 内置函数“return”和“exit”,后跟一个数字参数,$? 和参数,并终止执行。

    值得注意的是$?可以通过在子shell中调用exit来赋值,像这样:

    # (exit 259)
    # echo $?
    3
    

    【讨论】:

    • 如果有些人错过了,exit 259 回显为3,因为最终的退出值是一个字节。 259 % 256 = 3
    • "both $? with argument" 附近的句子是什么意思(似乎难以理解)?也许改写?请通过editing your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来像是今天写的)。
    【解决方案9】:

    首先return是关键字,exit是函数。

    也就是说,这里有一个最简单的解释。

    return

    它从函数中返回一个值。

    exit

    退出或放弃当前shell。

    【讨论】:

    • 不是真的!你在逻辑上是错误的。 Exit 是一个函数,而return 是一个关键字。返回不仅仅是退出代码,这就是比较不公平的原因。
    • 我已经对其进行了编辑,以使我想要表达的观点更加清晰。感谢您帮助我做到这一点。
    • exitreturn 都不是“关键字”,或者,正如 bash 手册所称,它们是“保留字”。从 bash 函数的意义上讲,两者都不是“函数”。两者都是 内置命令, 用 bash 术语表示。 (一个名为 exit() 的 C 标准库函数,并且 C 编程语言有一个保留字 return,但这些不应与 bash 命令混淆,尽管它们的语义很奇怪类似。)
    【解决方案10】:

    为其他一些答案添加可操作的方面:

    两者都可以给出退出代码 - 默认或由函数定义,唯一的“默认”为零表示退出和返回都成功。任何状态都可以有一个自定义数字 0-255,包括成功。

    Return 通常用于在当前 shell 中运行的交互式脚本,例如使用 . script.sh 调用,并且只是将您返回到调用 shell。然后调用 shell 可以访问返回代码 - $? 为您提供定义的返回状态。 在这种情况下,Exit 也会关闭您的 shell(包括 SSH 连接,如果您是这样工作的)。

    如果脚本是可执行的并且从另一个脚本或外壳调用并在子外壳中运行,则退出是必要的。然后调用 shell 可以访问退出代码 - 在这种情况下 return 会给出错误。

    【讨论】:

      【解决方案11】:

      如果将 Bash 脚本转换为函数,则通常将 exit N 替换为 return N。调用函数的代码会将返回值视为与子进程的退出代码相同。

      在函数内部使用exit会强制整个脚本结束。

      【讨论】:

        猜你喜欢
        • 2011-03-28
        • 2018-04-03
        • 2013-06-27
        • 1970-01-01
        • 1970-01-01
        • 2021-12-02
        • 1970-01-01
        • 2018-04-29
        • 2020-06-07
        相关资源
        最近更新 更多