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


429

就退出代码而言,Bash函数中的returnand exit语句有什么区别?


55
Protip:help <command>在您的shell中键入以获取有关shell内置功能的信息。你的情况help returnhelp exit
SiegeX

Answers:


318

man bash开始return [n];

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

...在exit [n]

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

编辑:

根据您对问题的编辑,关于退出代码,return与退出代码无关。退出代码仅适用于应用程序/脚本,而不适用于功能。因此,就此而言,设置脚本退出代码的唯一关键字(调用程序可以使用$?shell变量捕获的退出代码)为exit

编辑2:

我最近提到的声明exit引起了一些评论。这样做是为了区分returnexit理解OP,实际上,在程序/ shell脚本的任何给定点,这exit都是用退出代码结束调用过程结束脚本的唯一方法。

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

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

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

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


10
不完全是。它总是从当前shell返回一个值。是否在函数内部都没有关系。
迭戈塞维利亚

10
评论您的编辑:我可能会混淆返回值和退出代码,但回显func(){ return 50; };func;echo $?50。因此$?shell变量似乎并不限于exit
lecodesportif 2010年

8
$?扩展到最近执行的前台管道的退出状态。” 该退出可能是通过调用exit(或击中脚本的结尾)的形式或以调用return函数内部的形式从外壳中退出的。
SiegeX

11
@lecodesportif:$? 当前进程/脚本exit的结果仅限于此脚本执行的最后一条命令或结果。因此,如果最后一个脚本行是对该函数的调用,并且该函数返回50,是的$?,您对调用您的进程产生的那个是50。但是,这与无关return,因为这是限于当前脚本。仅当此函数调用是脚本的最后一句话时才返回它。exit但是,始终完成脚本并返回$? 有关调用过程的值。
迭戈塞维利亚

11
-1使我对“ return与退出代码无关”这一行感到困惑。实验告诉我,函数的返回代码和脚本的退出代码之间没有功能上的区别。
杰克

296

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()

17
很好的例子。您还可以在中显示退出值1 $?
迭戈塞维利亚

48
请注意,如果在调用“ retfunc”之前添加“ set -e”,此功能将不会打印“ We are here here”。
迈克尔

4
但是,echo fnord | while read x; do exitfunc; done; echo "still here"将打印“仍在此处”。while在这种情况下,似乎只有子外壳退出了。
2013年

一个近似的解决方法是,done || exit $?但这很丑陋,并且并非完全等效。
Tripleee 2013年

2
+1添加可能会很有用:``` return将导致当前函数或源脚本超出范围`'' 。
以撒

56

我认为没有人真正完全回答过这个问题,因为他们没有描述两者的用法。好的,我想我们知道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"

在此示例中,测试可用于指示是否找到目录。请注意,返回后的任何内容都不会在函数中执行。在外壳程序中,0是true,但false是1,与其他prog langs不同。

有关函数的更多信息:http : //www.linuxjournal.com/content/return-values-bash-functions

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


3
或只是使用test -d $1以达到相同的结果。永远不要做if <check> return else return<check>至少在我所知道的所有语言中,单单都会做同样的事情。
erikbwork 2015年

4
要更清楚地了解erik的意思:其isdirectory() { [ -d "$1" ]; }行为与您在此处的行为完全相同:shell函数的默认返回值,无论是到达其代码的结尾还是return不带任何参数的a,都是最新命令。
查尔斯·达菲

11
这里的其他评论者批评麦克·Q的例子风格,而实际上他正在谈论return声明的行为。的确,他的例子很简单,不用于生产。但这很简单,因此可以很好地完成他的任务。没错。
Mike S

谢谢Mike S,是的,我同意最简单的例子可以最好地解释退出与返回。其他评论当然是有效的,应该考虑用于更高级的bash编码器;-)
Mike Q

1
有人可能说这与问题不完全相关,但是它与我曾经导致我找到该问题的问题有关,并回答了这个问题。:-)
杰西·斯蒂尔

33

请记住,函数是脚本内部的函数,通常使用return语句从那里调用它们。调用外部脚本完全是另一回事,并且脚本通常以exit语句终止。

“相对于退出代码,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语句结尾,则最后执行的命令的状态将作为状态码返回(并将放置在中$?)。

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

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


25

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

. a.sh

如果您exit在中添加a.sh,它不仅会终止脚本,还会终止您的Shell会话。

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


1
但是,当我只运行a.sh时,我收到一个error return: can only 'return' from a function or sourced script,这使其不适合一般脚本。
彼得-恢复莫妮卡

在脚本的顶层,这两种all情况都不适合。在当前外壳中使用.source运行脚本,而不是生成子外壳。该脚本必须知道如何使用。对相反的用户不利。就个人而言,我建议在第一次运行脚本之前先阅读它们。
杰西·奇斯霍尔姆

3
我遇到的一个很棒的技巧是使用一个trap函数ERR EXIT,然后先保存失败命令的退出代码,errCode=$?然后退出脚本(无论是否提供),return $errCode || exit $errCode其中的||意思是“如果我因为不提供而无法返回, ,请退出。”
dragon788

6

用简单的话(主要是编码新手),我们可以说,

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

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

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

1
在bash脚本中,exit函数不多于return。它们是内置命令。他们甚至都不是保留字。
彼得-恢复莫妮卡

6
  • exit终止当前进程 ; 有或没有退出代码,都应将其视为系统而不是程序功能。请注意,在寻找资源时,exit将结束外壳程序,但是在运行时将仅exit脚本。

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

我想补充一点,在获取源代码的同时,要想exit在不破坏外壳的情况下从函数中编写脚本并不容易。我认为,以“测试”脚本为例更好

#!/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 -并且-外壳将关闭。

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都将退出。


在块中添加()放置该块,从而有效地撤消.(source)命令,就好像您已正常运行测试脚本一样,该命令位于子壳中。该脚本未与IOf一起运行,.或者source实际上没有2个子Shell。
杰西·奇斯霍尔姆

3

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

首先,需要进行一些澄清:

  • 终止(shell)的执行不需要(return |)语句。即使没有(return | exit)语句,(shell)也会在到达其代码列表的末尾时终止。
  • 不需要(返回)语句将值从终止(函数)传回。每个进程都有一个内置变量$?总是有一个数值。它是一个特殊变量,无法像“?= 1”那样设置,而只能以特殊方式设置(请参阅下面的*)。$的价值?在(称为function | sub shell)中要执行的最后一条命令之后的值是传递回(function caller | parent shell)的值。无论执行的最后一条命令是(“ return [n]” |“退出[n]”)还是普通命令(“ return”)或碰巧是被调用函数代码中的最后一条命令,都是如此。

在上面的项目符号列表中,始终从第一项或从第二项中选择“(x | y)”,以分别获取有关函数&return或shells&exit的语句。

清楚的是,它们都共享特殊变量$的通用用法吗?在值终止后向上传递值。

*现在以特殊方式表示$?可以设置:

  • 当被调用函数终止并返回到其调用者时,$?调用者中的最终值将等于$?在终止功能中。
  • 当父外壳程序隐式或显式地等待单个子外壳程序并通过终止该子外壳程序而释放时,则$?父外壳中的值将等于$?的最终值?在终止的子外壳中。
  • 一些内置函数可以修改$?取决于他们的结果。但是有些没有。
  • 内置函数“ return”和“ exit”,后跟数字参数$?带参数,并终止执行。

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

# (exit 259)
# echo $?
3  

4
如果有人遗漏了它,则会exit 259回显为,3因为最终退出值是一个字节。 259 % 256 = 3
杰西·奇斯霍尔姆

0

首先return是一个关键字exit我的朋友是一个函数。

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

return 它从函数返回一个值。

exit 它退出当前的shell或放弃当前的shell。


并不是的!你在逻辑上是错误的。退出是功能,return而是关键字。返回不仅限于退出代码,这就是为什么比较不公平的原因。
艾哈迈德·阿怀斯

我对它进行了编辑,以使观点更加清楚。感谢您的帮助。
艾哈迈德·阿怀斯

4
既不是“关键字”,exit也不return是bash手册所说的“保留字”。从bash函数的意义上讲,任何一个都不是“函数”。两者都是内置命令,使用bash术语。(这里被称为C标准库函数exit(),以及C编程语言有一个保留字return,但这些不应该与在bash命令混淆,即使它们的语义是好奇相似。)
彼得-恢复莫妮卡
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.