Bash函数中的返回值

min*_*dia 267 bash function return-value

我正在使用bash脚本,我想执行一个函数来打印返回值:

function fun1(){
  return 34
}
function fun2(){
  local res=$(fun1)
  echo $res
}
Run Code Online (Sandbox Code Playgroud)

当我执行时fun2,它不会打印"34".为什么会这样?

tam*_*gal 340

虽然bash有一个return语句,但你唯一能用它指定的是函数自身的exit状态(0和之间的值255,0表示"成功").所以return不是你想要的.

您可能希望将return语句转换为echo语句 - 这样可以使用$()大括号捕获函数输出,这似乎正是您想要的.

这是一个例子:

function fun1(){
  echo 34
}

function fun2(){
  local res=$(fun1)
  echo $res
}
Run Code Online (Sandbox Code Playgroud)

另一种获取返回值的方法(如果你只想返回0-255的整数)是$?.

function fun1(){
  return 34
}

function fun2(){
  fun1
  local res=$?
  echo $res
}
Run Code Online (Sandbox Code Playgroud)

另外,请注意,您可以使用返回值来使用布尔逻辑,fun1 || fun2只有fun2fun1返回0值时才会运行.默认返回值是函数中执行的最后一个语句的退出值.

  • 不,我需要该死的**返回值**.回声地狱 (120认同)
  • 为什么不使用`$?`? (9认同)
  • @Blauhirn在这个环境中,使用这个`||`结构,退出代码为0被认为是成功的,因此是"true".非零是错误,因此是错误的.想想'fun1 || fun2`作为"if fun1返回成功或fun2返回成功"的简写,而不是操作符对实际返回值本身. (6认同)
  • 令人烦恼的是,应该提供数据的函数也不能将其他内容回显到stdout,因为使用$()的调用者也会收到它并且会混淆或者必须解析输出.全局变量并不是很好,因为在两个碰巧嵌套且数据丢失的地方使用相同的全局变量只是时间问题.应该有单独的通道来打印数据和发回数据. (6认同)
  • 你需要执行`fun1`然后返回值存储在`$?`中.虽然我不建议那样做...... (2认同)
  • `乐趣1 || 如果 fun1 返回 0 值,fun2 只会运行 fun2。` 不应该是`非 0 值`吗? (2认同)
  • @meowsqueak抱歉,我不明白.`如果fun1返回成功或者fun2返回成功`.所以,如果`fun1`已经返回成功,那么'fun2`将不会被执行!对?因此,`fun1`需要为`fun2`返回`NON-0-value`才能运行. (2认同)
  • @Blauhirn是的,我认为您是对的。如您所说,它应该是“非零值”,或者运算符应该是“ &&”而不是“ ||”。我之前的评论不正确。 (2认同)

Ign*_*ams 65

$(...)捕获由其中包含的命令发送到stdout的文本.return不输出到stdout.$?包含最后一个命令的结果代码.

fun1 (){
  return 34
}

fun2 (){
  fun1
  local res=$?
  echo $res
}
Run Code Online (Sandbox Code Playgroud)

  • 是`return`用于设置`$?`这是`退出状态`.在上面的例子中,`fun1`的`退出状态`将是'34`.另外,请注意`$(...)`除了指定命令的stdout之外还捕获stderr. (4认同)

And*_*ler 53

Bash中的函数不是像其他语言那样的函数; 他们实际上是命令.因此,函数就像是从路径中获取的二进制文件或脚本一样.从程序逻辑的角度来看,应该没有什么区别.

Shell命令通过管道(也称为流)连接,而不是基本或用户定义的数据类型,如"真实"编程语言.没有像命令的返回值这样的东西,可能主要是因为没有真正的方法来声明它.它可能出现在手册页或--help命令的输出上,但两者都只是人类可读的,因此被写入风中.

当命令想要获取输入时,它从其输入流或参数列表中读取它.在这两种情况下,都必须解析文本字符串.

当一个命令想要将它拥有的东西返回echo到它的输出流时.另一种经常实践的方法是将返回值存储在专用的全局变量中.写入输出流更清晰,更灵活,因为它也可以采用二进制数据.例如,您可以轻松返回BLOB:

encrypt() {
    gpg -c -o- $1 # encrypt data in filename to stdout (asks for a passphrase)
}

encrypt public.dat > private.dat # write function result to file
Run Code Online (Sandbox Code Playgroud)

正如其他人在此线程中编写的那样,调用者也可以使用命令替换$()来捕获输出.

同时,该函数将"返回" gpg(GnuPG)的退出代码.将退出代码视为其他语言没有的奖励,或者根据您的性情,将其视为shell函数的"Schmutzeffekt".按照惯例,此状态在成功时为0,或者在其他情况下为1-255范围内的整数.为了清楚起见:return(like exit)只能取0到025之间的值,而0之外的值不一定是错误,通常是断言.

当您没有提供显式值时return,状态取自Bash语句/函数/命令中的最后一个命令,等等.所以始终存在状态,并且return只是提供它的简单方法.

  • +1 用于解释 shell 编程是关于通过管道连接命令。其他编程语言通过返回类型组合函数。Bash 通过文本流组合命令。 (7认同)
  • 如果一个函数必须同时完成这两项任务怎么办?也就是说,从脚本发送一些输出,并生成一些文本作为其返回值,这些文本不应受到该函数必须记录到脚本标准输出的任何内容的干扰。 (3认同)
  • +1,用于解释函数与命令以及这如何影响将数据发送回调用方的概念 (2认同)

Aus*_*ips 26

return语句设置函数的退出代码,exit与整个脚本的退出代码相同.

最后一个命令的退出代码始终在$?变量中可用.

function fun1(){
  return 34
}

function fun2(){
  local res=$(fun1)
  echo $? # <-- Always echos 0 since the 'local' command passes.

  res=$(fun1)
  echo $?  #<-- Outputs 34
}
Run Code Online (Sandbox Code Playgroud)

  • `res` 的值是多少? (3认同)

Tom*_*ndt 11

作为其他人优秀帖子的补充,这里有一篇总结这些技术的文章:

  • 设置全局变量
  • 设置一个全局变量,你传递给函数的名字
  • 设置返回代码(并用 $ 选择它?)
  • “回显”一些数据(并使用 MYVAR=$(myfunction) 获取)

从 Bash 函数返回值


Oli*_*ver 10

其他答案的问题是它们要么使用全局(全局),而在调用链中有多个函数时可以覆盖全局,或者echo这意味着您的函数无法输出诊断信息(您会忘记函数执行此操作以及“结果”,即返回值,将包含比呼叫者期望的更多的信息,从而导致奇怪的错误),或者eval过于繁琐。

这样做的正确方法是将顶层内容放入函数中,并使用local带有bash的动态作用域规则。例:

func1() 
{
    ret_val=hi
}

func2()
{
    ret_val=bye
}

func3()
{
    local ret_val=nothing
    echo $ret_val
    func1
    echo $ret_val
    func2
    echo $ret_val
}

func3
Run Code Online (Sandbox Code Playgroud)

这个输出

nothing
hi
bye
Run Code Online (Sandbox Code Playgroud)

动态作用域意味着ret_val根据调用者指向不同的对象!这不同于词法作用域,这是大多数编程语言所使用的。这实际上是一个已记录的功能,很容易遗漏,并且没有很好地解释,这里是该功能的文档(强调是我的):

函数本地变量可以使用本地内置声明。这些变量仅对函数及其调用的命令可见。

对于具有C / C ++ / Python / Java / C#/ javascript背景的人来说,这可能是最大的障碍:bash中的函数不是函数,它们是命令,并且具有如下行为:它们可以输出到stdout/ stderr,可以通过管道传递/ out,他们可以返回退出代码。基本上,在脚本中定义命令与创建可以从命令行调用的可执行文件之间没有区别。

因此,不要像这样编写脚本:

top-level code 
bunch of functions
more top-level code
Run Code Online (Sandbox Code Playgroud)

这样写:

# define your main, containing all top-level code
main() 
bunch of functions
# call main
main  
Run Code Online (Sandbox Code Playgroud)

where main()声明ret_val为as local,所有其他函数均通过返回值ret_val

另请参见以下Unix&Linux问题:Shell函数中的局部变量的范围

ya.teck发布的是一种根据情况根据情况甚至可能更好的解决方案local -n


ya.*_*eck 10

实现此目的的另一种方法是名称引用(需要Bash 4.3+)。

function example {
  local -n VAR=$1
  VAR=foo
}

example RESULT
echo $RESULT
Run Code Online (Sandbox Code Playgroud)

  • 哇,我对此一无所知,谢谢分享。 (3认同)
  • 有人想知道 `-n &lt;name&gt;=&lt;reference&gt;` 的作用:使新创建的变量成为对 `&lt;reference&gt;` 指向的另一个变量的引用。对引用的变量执行对“&lt;name&gt;”的进一步赋值。 (3认同)

Ahm*_*yed 9

echo我能想到的最简单的方法是在方法体中使用,如下所示

get_greeting() {
  echo "Hello there, $1!"
}

STRING_VAR=$(get_greeting "General Kenobi")
echo $STRING_VAR
# Outputs: Hello there, General Kenobi!
Run Code Online (Sandbox Code Playgroud)


doc*_*doc 6

如果在定义函数的脚本中运行,我喜欢执行以下操作:

POINTER= # used for function return values

my_function() {
    # do stuff
    POINTER="my_function_return"
}

my_other_function() {
    # do stuff
    POINTER="my_other_function_return"
}

my_function
RESULT="$POINTER"

my_other_function
RESULT="$POINTER"
Run Code Online (Sandbox Code Playgroud)

我喜欢这个,因为如果我愿意,我可以在我的函数中包含echo语句

my_function() {
    echo "-> my_function()"
    # do stuff
    POINTER="my_function_return"
    echo "<- my_function. $POINTER"
}
Run Code Online (Sandbox Code Playgroud)