468

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

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

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

4

10 回答 10

563

尽管 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时才会运行。默认返回值是函数内执行的最后一条语句的退出值。fun2fun10

于 2013-06-27T07:19:19.503 回答
90

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

fun1 (){
  return 34
}

fun2 (){
  fun1
  local res=$?
  echo $res
}
于 2013-06-27T07:24:16.973 回答
82

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

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

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

当一个命令想要返回一些东西时,它必须把echo它返回到它的输出流中。另一种常用的方法是将返回值存储在专用的全局变量中。写入输出流更清晰、更灵活,因为它也可以接收二进制数据。例如,您可以轻松地返回一个BLOB

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

encrypt public.dat > private.dat # Write the function result to a file

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

Parallely,该函数将“返回” gpgGnuPG)的退出代码。将退出代码视为其他语言没有的奖励,或者根据您的气质,将其视为 shell 函数的“Schmutzeffekt”。按照惯例,此状态为 0 表示成功,或 1-255 范围内的整数表示其他值。为了清楚起见:return(like exit) 只能取 0-255 之间的值,而 0 以外的值不一定是错误,正如经常断言的那样。

当您不提供显式值时return,状态将取自 Bash 语句/函数/命令等中的最后一个命令。所以总是有一个状态,并且return只是提供它的一种简单方法。

于 2013-06-27T08:31:42.850 回答
50

其他答案的问题是它们要么使用全局,当调用链中有多个函数时可以覆盖它,或者echo这意味着你的函数无法输出诊断信息(你会忘记你的函数这样做和“结果”,即返回值,将包含比调用者预期更多的信息,从而导致奇怪的错误),或者eval太重和太笨拙。

正确的做法是将顶层的东西放在一个函数中,并使用localBash 的动态范围规则。例子:

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指向不同的对象,具体取决于调用者!这与大多数编程语言使用的词法作用域不同。这实际上是一个记录在案的功能,只是很容易错过,而且解释得不是很好。这是它的文档(重点是我的):

函数的局部变量可以用 local 内建函数声明。这些变量仅对函数及其调用的命令可见。

对于具有 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

wheremain()声明ret_valaslocal和所有其他函数通过ret_val.

另请参阅Unix 和 Linux问题Scope of Local Variables in Shell Functions

另一种可能更好的解决方案取决于具体情况,是ya.teck 发布的使用local -n.

于 2018-08-29T16:26:23.790 回答
37

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
}
于 2013-06-27T07:25:15.680 回答
33

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

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

example RESULT
echo $RESULT
于 2018-10-06T10:48:39.940 回答
14

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

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

从 Bash 函数返回值

于 2018-07-30T21:04:26.707 回答
9

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

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"
}
于 2015-07-03T11:21:07.747 回答
3

您可以创建一个修改输入参数的函数,而不是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. 返回(设置)多个变量。
于 2021-05-24T14:11:09.300 回答
-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:
于 2017-09-13T15:17:43.213 回答