ChatGPT解决这个技术问题 Extra ChatGPT

Bash函数中返回和退出的区别

Bash 函数中的 returnexit 语句在退出代码方面有什么区别?

提示:在您的 shell 中键入 help <command> 以获取有关 shell 内置功能的信息。在您的情况下 help returnhelp exit
Protip #2:在您的 shell 中输入 type <command> 以获取有关它是否是内置 Bash 的信息。
如果您想退出有源或无源的脚本,您可以执行以下操作:return 2> /dev/null | exit。它将首先尝试返回,如果不能返回,它不会显示任何错误并使用退出。
提示 #4:man exit
help () {sh -c "help $*"} 放入您的 .zshenv

T
The_Modeler

man bashreturn [n]

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

...在 exit [n] 上:

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

编辑:

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

编辑2:

我最后提到 exit 的声明引起了一些评论。为了理解 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 与退出代码无关”这一行感到困惑。实验告诉我,函数的返回码和脚本的退出码在功能上没有区别。
N
Nathan Arthur

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 子外壳。
+1 添加:``` return 将导致当前函数或源脚本超出范围可能会很有用```。
请注意,在上面的示例中,如果您使用 set -e 运行以便脚本在第一个错误时退出,它将在第一个函数调用返回非零值后退出。
P
Peter Mortensen

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

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 示例的风格,而实际上他是在谈论return语句的行为。确实,他的示例过于简单,不能用于生产。但这很简单,所以它确实很好地完成了他的任务。它没有错。
谢谢 Mike S,是的,我同意最简单的例子最好地解释了退出与返回。其他评论当然是有效的,应该考虑用于更高级的 bash 编码器;-)
@erikbwork 嗯,这是大多数学习材料中的常见做法。作为妥协,我根据您的意见在帖子中添加了免责声明。
P
Peter Mortensen

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

“关于退出代码的 Bash 函数中的 return 和 exit 语句之间的差异”非常小。两者都返回一个状态,而不是本身的值。状态为零表示成功,而任何其他状态(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 语句的情况下结束,则最后执行的命令的状态将作为状态码返回(并将放置在 $? 中)。

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

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


k
kenorb

有时,您使用 .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 退出脚本(源代码或非源代码),其中 {5 } 的意思是“如果我因为没有来源而无法返回,那就退出吧”。
P
Peter Mortensen

exit 终止当前进程;无论有没有退出代码,都将其视为系统而不是程序功能。请注意,在采购时,exit 将结束 shell。但是,在运行时,它只会退出脚本。

从函数返回 调用后返回指令,带或不带返回码。 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 中,有效地取消执行 .(源)命令,就像您正常运行测试脚本一样,该脚本位于子 shell 中。 IOf 脚本未使用 .source 运行,那么您实际上有 2 个子 shell。
P
Peter Mortensen

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

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

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

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

在 bash 脚本中,exitreturn 差不多是一个函数。它们是内置命令。它们甚至不是保留字。
C
Craig Hicks

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 中将等于 $? 的最终值?在终止的子外壳中。

一些内置函数可以修改 $?取决于他们的结果。但有些没有。

内置函数“return”和“exit”,当后跟一个数字参数时都设置 $?与他们的论点,并终止执行。

值得注意的是,$? 可以通过在子 shell 中调用 exit 来赋值,如下所示:

# (exit 259)
# echo $?
3

如果有些人错过了它,exit 259 会作为 3 回显,因为最终退出值是单个字节。 259 % 256 = 3
"both $? with argument" 附近的句子是什么意思(似乎难以理解)?也许改写?请通过 editing your answer 回复,而不是在评论中(没有“编辑:”、“更新:”或类似内容 - 答案应该看起来好像是今天写的)。
我猜这句话试图说明 exitreturn 的参数存储在 $? 中。
P
Peter Mortensen

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

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

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

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


P
Peter Mortensen

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

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


P
Peter Mortensen

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

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

return

它从函数返回一个值。

exit

它退出或放弃当前的 shell。


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