Bash函数中的返回值

时间:2013-06-27 07:17:30

标签: bash function return-value

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

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

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

11 个答案:

答案 0 :(得分:294)

虽然bash有一个return语句,但你可以用它指定唯一的东西是函数自己的exit状态(0255之间的值,0表示“成功”)。所以return不是你想要的。

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

以下是一个例子:

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。默认返回值是函数内执行的最后一个语句的退出值。

答案 1 :(得分:60)

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

fun1 (){
  return 34
}

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

答案 2 :(得分:43)

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范围内的整数。为清楚起见:return(如exit)只能取0到025之间的值,而0之外的值不一定是错误,通常是断言。

如果未使用return提供显式值,则会从Bash语句/函数/命令中的最后一个命令获取状态,依此类推。所以始终存在状态,return只是提供它的简单方法。

答案 3 :(得分:23)

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
}

答案 4 :(得分:5)

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

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"
}

答案 5 :(得分:5)

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

执行此操作的正确方法是将顶级内容放入函数中,并使用具有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根据调用者指向不同的对象!这不同于词法作用域,这是大多数编程语言所使用的。这实际上是a documented feature,很容易遗漏,也没有很好解释,这是它的文档(重点是我的):

  

函数局部变量可以用局部变量声明   内置的。这些变量仅对功能 和   

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

所以不要像这样编写脚本:

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为本地,所有其他函数通过ret_val返回值。

另请参见https://unix.stackexchange.com/questions/282557/scope-of-local-variables-in-shell-functions

根据情况,另一种甚至更好的解决方案是使用local -n的{​​{3}}。

答案 6 :(得分:5)

实现这一目标的另一种方法是name references(需要Bash 4.3 +)。

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

example RESULT
echo $RESULT

答案 7 :(得分:3)

作为其他人的精彩文章的补充,这是一篇概述这些技术的文章:

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

Returning Values from Bash Functions

答案 8 :(得分:0)

您可以创建一个使用 var=$(func) 修改输入参数的函数,而不是使用整个函数输出调用 eval

var1="is there"
var2="anybody"

function modify_args() {
    echo "Modifying first argument"
    eval $1="out"
    
    echo "Modifying second argument"
    eval $2="there?"
}

modify_args var1 var2
# Prints "Modifying first argument" and "Modifying second argument"
# Sets var1 = out
# Sets var2 = there?

如果您需要,这可能很有用:

  1. 打印到函数范围内的 stdout/stderr(不返回)
  2. 返回(设置)多个变量。

答案 9 :(得分:-1)

你可以这样做:

return_it(){

    eval ${FUNCNAME[1]}_r_val="\$1"

}

然后像这样在你的函数中使用它:

fun1(){
    return_it 34
}

fun2(){
    fun1; echo $fun1_r_val
}

答案 10 :(得分:-2)

使用多个返回值的数组 Windows

Git Bash

BASH CODE:

#!/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: