bash函数的隐式返回?


11

说我有一个bash函数,如下所示:

gmx(){
  echo "foo";
}

此函数将隐式返回echo命令的退出值,还是使用return是必需的?

gmx(){
  echo "foo";
  return $?
}

我假设bash的工作方式是,bash函数的最后一条命令的退出状态是“返回”的状态,但不能100%确定。

Answers:


10

return做一个明确的从一个shell函数或“点脚本”(一源脚本)的回报。如果return未执行,则在shell函数或点脚本的末尾隐式返回。

如果return执行时不带参数,则等同于返回最近执行的命令的退出状态。

这就是return所有POSIX Shell中的工作方式。

例如,

gmx () {
  echo 'foo'
  return "$?"
}

因此等于

gmx () {
  echo 'foo'
  return
}

这与

gmx () {
  echo 'foo'
}

通常,很少需要使用$?。实际上,仅在需要保存它以备将来使用时才需要它,例如,如果您需要多次研究它的值(在这种情况下,您将其值分配给一个变量并对该变量执行一系列测试)。


2
使用的缺点之一return是对于像这样定义的函数f() (...; cmd; return),它阻止了一些外壳cmd程序在与子外壳程序相同的过程中运行的优化。对于许多外壳程序,这也意味着该函数的退出状态不包含cmd已被杀死的信息(大多数外壳程序无论如何都无法检索该信息)。
斯特凡Chazelas

1
请注意,使用pdksh及其某些派生工具(如OpenBSD shposh),return -- "$?"如果有可能最后一条命令是返回负数的函数,则需要使用。mksh(同样基于pdksh)禁止函数返回负值。
斯特凡Chazelas

谢谢您的帮助,我想我不了解return x功能与exit x...的不同之处。我唯一了解的是return x不会退出当前流程。
亚历山大·米尔斯

@AlexanderMills好吧,这就是我说的:return用于从函数或点脚本中返回。exit做一些完全不同的事情(终止一个过程)。
库沙兰丹

是的,这很有意义,我想我对此已经有了更好的了解
Alexander Mills

7

bash(1)手册页:

执行时,功能的退出状态是主体中最后执行的命令的退出状态。


对,结果必然是return语句只不过是退出状态?
亚历山大·米尔斯

我猜这return是一个内置命令-尽管return 1exit 1等等有所不同
亚历山大·米尔斯

1
“ return [n]:使函数停止执行并将n所指定的值返回给其调用方。如果省略n,则返回状态为函数体内执行的最后一条命令的状态。” (同上)因此,return如果已指定,则将函数的退出状态强制为特定值。
伊格纳西奥·巴斯克斯

1
@AlexandwrMills是的,return并且exit都是内置的,除了return只能在函数中使用。您不能使用终止脚本return。退出状态是命令返回的值。return是返回该值的命令。因此,“返回语句只不过是退出状态”就不够准确。一个是值,另一个是命令加值。
Sergiy Kolodyazhnyy

1
@AlexanderMills,return从函数返回,exit退出整个外壳。这是完全一样的,说下与return对比exit(n),或者returnsys.exit()Python编写的。
ilkkachu

2

我将在已经提供的答案中添加一些注意事项:

  • 即使return对shell有非常特殊的意义,从语法的角度来看,它也是shell内置命令,并且return语句像其他任何简单命令一样被解析。因此,这意味着像在任何其他命令的参数中一样,$?如果不加引号,将受到split + glob的约束

    因此,您需要引用它$?来避免它:

    return "$?"
  • return通常不接受任何选项(ksh93的接受通常的--help--man--author...虽然)。它期望的唯一参数(可选)是返回码。接受的返回码范围因外壳而异,并且是否正确反映0..255以外的任何值$?也因外壳而异。请参阅进程终止时的默认退出代码?有关详细信息。

    大多数shell接受负数(毕竟,传递给_exit()/ exitgroup()system调用的参数是an int,因此其值至少包含-2 31到2 31 -1,因此仅使shell接受相同范围的函数才有意义) 。

    大多数shell使用waitpid()和。用于检索退出状态的API,但是在这种情况下,当存储在中时,它会被截断为0到255之间的数字$?。即使调用一个函数不涉及生成一个进程并用于waitpid()检索其退出状态,因为所有操作都是在同一进程中完成的,但是许多shell也模仿了waitpid()调用函数时的行为。这意味着即使您return使用负值调用,$?也将包含正数。

    现在,在那些return接受负数(ksh88,ksh93,bash,zsh,pdksh以及mksh,yash以外的派生数)的shell中,有一些(pdksh和yash)需要将其写成return -- -123其他形式,-123取为三个-1-2-3无效的选项。

    由于pdksh及其派生词(例如OpenBSD shposh)在中保留了负数$?,这意味着return "$?"$?包含负数时操作将失败(当最后一个运行命令是返回负数的函数时,将发生这种情况)。

    因此return -- "$?"在这些shell中会更好。但是请注意,尽管大多数外壳程序都支持该语法,但该语法不是POSIX,实际上不受mkshash派生工具支持。

    因此,总而言之,对于基于pdksh的shell,您可以在函数的参数中使用负数,但如果这样做,return "$@"将无法正常工作。在其他Shell中,return "$@"它将起作用,并且您应避免使用负数(或0..255以外的数字)作为。的参数return

  • 在我所知道的所有shell中,return从在函数内部运行的子shell内部调用将导致该子shell退出(具有提供的退出状态(如果有的话,或最后一次运行命令的退出状态)),但不会导致该函数的返回(对我而言,尚不清楚POSIX是否为您提供保修,有人认为exit应使用POSIX 代替功能内的出口子外壳。例如

    f() {
      (return 3)
      echo "still inside f. Exit status: $?"
    }
    f
    echo "f exit status: $?"

    将输出:

    still inside f. Exit status: 3
    f exit status: 0

0

是的,函数的隐式返回值是最后执行的命令的退出状态。在任何shell脚本的任何时候都是如此。在脚本执行顺序中的任何时候,当前退出状态都是最后执行的命令的退出状态。偶数命令作为变量分配的一部分执行:var=$(exit 34)。与函数的区别在于,函数可以在函数执行结束时更改退出状态。

更改“当前退出状态”的另一种方法是启动子shell并以任何所需的退出状态退出:

$ $(exit 34)
$ echo "$?"
34

是的,确实需要引用退出状态扩展

$ IFS='123'
$ $(exit 34)
$ echo $?
4

一个(exit 34)也工作。
有人可能会争辩说应该是更健壮的构造$(return 34),而退出应该“退出”正在执行的脚本。但$(return 34)不适用于任何版本的bash。因此,它不是便携式的。

设置退出状态的最安全方法是使用它,因为它是为工作,定义和return从功能而设计的:

exitstatus(){ return "${1:-"$?"}"; }

因此,在功能末尾。它是完全等同于有存在或是returnreturn "$?"。函数的结尾不必表示“函数的最后代码行”。

#!/bin/sh
exitstatus(){ a="${1:-"$?"}"; return "$a"; }
gmx(){
    if     [ "$1" = "one" ]; then
           printf 'foo ';
           exitstatus 78
           return "$?"
    elif   [ "$1" = "two" ]; then
           printf 'baz ';
           exitstatus 89
           return
    else
           printf 'baz ';
           exitstatus 90
    fi
}  

将打印:

$ ./script
foo 78
baz 89
baz 90

唯一实际的用途"$?"是打印其值:echo "$?"或将其存储在变量中(因为它是一个临时值,并且在执行的每个命令中都会更改):(exitstatus=$?请记住在类似的命令中引用该变量export EXITSTATUS="$?"

在该return命令中,值的有效范围通常为0到255,但是请理解,126 + n某些shell使用的值来表示特殊的退出状态,因此,一般建议使用0-125。

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.