Bash函数中的返回值


303

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

function fun1(){
  return 34
}
function fun2(){
  local res=$(fun1)
  echo $res
}

当我执行时fun2,它不会打印“ 34”。为什么会这样呢?


8
return在您的情况下,基本上与的exit code范围相同0 - 255echo按照@septi的建议使用。退出代码可以使用捕获$?
devnull

1
在这种情况下,在fun1中已经使用echo会更加灵活。这是unix编程的思想:echo将结果发送到标准输出,然后可以由res = $(fun1)的其他函数重用-或直接通过管道传递给其他函数:function a() { echo 34; } function b() { while read data; do echo $data ; done ;} a | b
Arne Babenhauserheide 2014年

这样做的正确方法是将顶层内容放入函数中,并使用具有bash动态范围规则的局部变量。我将创建一个答案来演示它,它不是一项众所周知的功能,而是一个完全受支持的功能。
奥利弗


Answers:


372

尽管bash有一条return语句,但您只能用它指定函数的自身exit状态(介于0和之间的值255,0表示“成功”)。所以return不是您想要的。

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

这是一个例子:

function fun1(){
  echo 34
}

function fun2(){
  local res=$(fun1)
  echo $res
}

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

function fun1(){
  return 34
}

function fun2(){
  fun1
  local res=$?
  echo $res
}

另外请注意,您可以使用返回值来使用布尔逻辑一样fun1 || fun2将只运行fun2,如果fun1返回一个0值。默认返回值是函数中最后执行的语句的退出值。


2
您需要执行fun1,然后将返回值存储在中$?。尽管我不建议您这样做…
tamasgal

9
为什么不使用$?
2014年

147
不,我需要该死的返回值。到地狱回声。
托马什Zato -恢复莫妮卡

7
在这种环境下,使用此||构造,@ Blauhirn 将退出代码0视为成功,因此为“ true”。非零是错误,因此为假。可以将其fun1 || fun2视为“如果fun1返回成功或fun2返回成功”的简写,而不是实际返回值本身的运算符。
大卫

6
令人烦恼的是,应该提供数据的函数也不能将其他内容回显到stdout,因为使用$()的调用者也将收到该信息,并且感到困惑或不得不解析输出。全局变量并不是很好,因为在两个可能嵌套的地方使用相同的全局变量只是时间问题,并且数据可能会丢失。应该有单独的通道来打印数据和发送数据。
奥利弗

68

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

fun1 (){
  return 34
}

fun2 (){
  fun1
  local res=$?
  echo $res
}

5
return用于设置$?这是exit status。在上述例子中,fun1exit status将是34。另外,请注意,$(...)除了从指定命令中输出stdout之外,还捕获stderr。
swoop81

58

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

就像其他人在该线程中编写的一样,调用者也可以使用命令替换$()来捕获输出。

并行地,该函数将“返回” gpg(GnuPG)的退出代码。将退出代码视为其他语言所没有的奖励,或者视您的性格而定,将其作为shell函数的“ Schmutzeffekt”。按照惯例,此状态为0表示成功,否则为1-255范围内的整数。为了明确起见:(returnexit)只能接受0-255之间的值,而0以外的值不一定是错误的,正如经常断言的那样。

当您不提供显式值时return,状态取自Bash语句/函数/命令中的最后一条命令,依此类推。因此,总有一种状态,return这只是提供状态的一种简便方法。


3
+1,用于解释功能与命令以及这如何影响将数据发送回调用方的概念
Oliver

3
+1用于解释shell编程是关于通过管道连接命令。其他编程语言通过返回类型组成函数。Bash通过文本流编写命令。
jrahhali '18

29

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
}

19

其他答案的问题是它们要么使用全局(全局),而当调用链中有多个函数时,可以覆盖全局;或者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

这个输出

nothing
hi
bye

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

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

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

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

top-level code 
bunch of functions
more top-level code

这样写:

# define your main, containing all top-level code
main() 
bunch of functions
# call main
main  

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

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

ya.teck发表的一个使用,这可能是根据情况的另一种甚至更好的解决方案local -n


16

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

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

example RESULT
echo $RESULT

2
任何人都想知道这-n <name>=<reference>是怎么回事:使新创建的变量成为对指向的另一个变量的引用<reference><name>对所引用变量的进一步分配。
瓦莱里奥

7

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

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"

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

my_function() {
    echo "-> my_function()"
    # do stuff
    POINTER="my_function_return"
    echo "<- my_function. $POINTER"
}

4

作为其他人的出色文章的补充,以下是总结这些技术的文章:

  • 设置一个全局变量
  • 设置一个全局变量,您将其名称传递给该函数
  • 设置返回码(并用$进行提取?)
  • 回显一些数据(并使用MYVAR = $(myfunction)进行拾取)

从Bash函数返回值


这是最好的答案,因为本文彻底讨论了所有选项。
mzimmermann

-2

Windows上的Git Bash使用多个数组返回值

现金代码:

#!/bin/bash

##A 6-element array used for returning
##values from functions:
declare -a RET_ARR
RET_ARR[0]="A"
RET_ARR[1]="B"
RET_ARR[2]="C"
RET_ARR[3]="D"
RET_ARR[4]="E"
RET_ARR[5]="F"


function FN_MULTIPLE_RETURN_VALUES(){

   ##give the positional arguments/inputs
   ##$1 and $2 some sensible names:
   local out_dex_1="$1" ##output index
   local out_dex_2="$2" ##output index

   ##Echo for debugging:
   echo "running: FN_MULTIPLE_RETURN_VALUES"

   ##Here: Calculate output values:
   local op_var_1="Hello"
   local op_var_2="World"

   ##set the return values:
   RET_ARR[ $out_dex_1 ]=$op_var_1
   RET_ARR[ $out_dex_2 ]=$op_var_2
}


echo "FN_MULTIPLE_RETURN_VALUES EXAMPLES:"
echo "-------------------------------------------"
fn="FN_MULTIPLE_RETURN_VALUES"
out_dex_a=0
out_dex_b=1
eval $fn $out_dex_a $out_dex_b  ##<--Call function
a=${RET_ARR[0]} && echo "RET_ARR[0]: $a "
b=${RET_ARR[1]} && echo "RET_ARR[1]: $b "
echo
##----------------------------------------------##
c="2"
d="3"
FN_MULTIPLE_RETURN_VALUES $c $d ##<--Call function
c_res=${RET_ARR[2]} && echo "RET_ARR[2]: $c_res "
d_res=${RET_ARR[3]} && echo "RET_ARR[3]: $d_res "
echo
##----------------------------------------------##
FN_MULTIPLE_RETURN_VALUES 4 5  ##<---Call function
e=${RET_ARR[4]} && echo "RET_ARR[4]: $e "
f=${RET_ARR[5]} && echo "RET_ARR[5]: $f "
echo
##----------------------------------------------##


read -p "Press Enter To Exit:"

预期的输出:

FN_MULTIPLE_RETURN_VALUES EXAMPLES:
-------------------------------------------
running: FN_MULTIPLE_RETURN_VALUES
RET_ARR[0]: Hello
RET_ARR[1]: World

running: FN_MULTIPLE_RETURN_VALUES
RET_ARR[2]: Hello
RET_ARR[3]: World

running: FN_MULTIPLE_RETURN_VALUES
RET_ARR[4]: Hello
RET_ARR[5]: World

Press Enter To Exit:
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.