我正在使用 bash 脚本,我想执行一个函数来打印返回值:
function fun1(){
return 34
}
function fun2(){
local res=$(fun1)
echo $res
}
当我执行 fun2
时,它不会打印“34”。为什么会这样?
return
与 exit code
基本相同,范围从 0 - 255
。按照@septi 的建议使用 echo
。可以使用 $?
捕获退出代码。
function a() { echo 34; }
function b() { while read data; do echo $data ; done ;}
a | b
尽管 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
将仅在 fun1
返回非 0
值时运行 fun2
。默认返回值是函数内执行的最后一条语句的退出值。
$(...)
捕获由其中包含的命令发送到标准输出的文本。 return
不输出到标准输出。 $?
包含最后一条命令的结果代码。
fun1 (){
return 34
}
fun2 (){
fun1
local res=$?
echo $res
}
return
用于设置 $?
,即 exit status
。在上面的示例中,fun1
的 exit status
将是 34
。另外,请注意 $(...)
除了从指定的命令中捕获标准输出之外,还捕获标准错误。
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
正如其他人在此线程中所写,调用者还可以使用命令替换 $()
来捕获输出。
并行地,该函数将“返回”gpg
(GnuPG) 的退出代码。将退出代码视为其他语言没有的奖励,或者根据您的气质,将其视为 shell 函数的“Schmutzeffekt”。按照惯例,此状态为 0 表示成功,或 1-255 范围内的整数表示其他值。为了明确这一点:return
(如 exit
)只能取 0-255 之间的值,而 0 以外的值不一定是错误,正如经常断言的那样。
当您没有为 return
提供显式值时,状态将取自 Bash 语句/函数/命令等中的最后一个命令。所以总是有一个状态,return
只是提供它的一种简单方法。
其他答案的问题是它们要么使用全局,当调用链中有多个函数时可以覆盖它,或者 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
指向不同的对象,具体取决于调用者!这与大多数编程语言使用的词法作用域不同。这实际上是 a documented feature,只是很容易错过,而且解释得不是很好。这是它的文档(重点是我的):
函数的局部变量可以用 local 内建函数声明。这些变量仅对函数及其调用的命令可见。
对于具有 C、C++、Python、Java、C# 或 JavaScript 背景的人来说,这可能是最大的障碍:bash 中的函数不是函数,它们是命令,并且行为如下:它们可以输出到 stdout
/{ 2},他们可以通过管道输入/输出,并且可以返回退出代码。基本上,在脚本中定义命令和创建可以从命令行调用的可执行文件之间没有任何区别。
因此,不要像这样编写脚本:
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
声明为 local
,所有其他函数都通过 ret_val
返回值。
另请参阅Unix & Linux 问题Scope of Local Variables in Shell Functions。
另一种可能更好的解决方案(视情况而定)是使用 local -n
的 posted by ya.teck。
local
内置函数,究竟会有什么不同? (因为这几乎就是我有时使用它的方式..)
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
}
res
的值是多少?
$res
始终为空,因为 fun1
没有标准输出
实现此目的的另一种方法是 name references(需要 Bash 4.3+)。
function example {
local -n VAR=$1
VAR=foo
}
example RESULT
echo $RESULT
-n <name>=<reference>
做什么的人:使新创建的变量成为对 <reference>
指向的另一个变量的引用。对引用的变量执行对 <name>
的进一步赋值。
-n
标志。
作为其他优秀文章的补充,这里有一篇总结这些技术的文章:
设置一个全局变量
设置一个全局变量,您将其名称传递给函数
设置返回码(并用 $ 提取它?)
'echo' 一些数据(并使用 MYVAR=$(myfunction) 获取)
Returning Values from Bash Functions
如果在定义函数的脚本中运行,我喜欢执行以下操作:
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"
}
您可以创建一个使用 eval
修改输入参数的函数,而不是使用整个函数输出调用 var=$(func)
,
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?
如果您需要:
在函数范围内打印到 stdout/stderr(不返回)返回(设置)多个变量。
我能想到的最简单的方法是像这样在方法体中使用 echo
get_greeting() {
echo "Hello there, $1!"
}
STRING_VAR=$(get_greeting "General Kenobi")
echo $STRING_VAR
# Outputs: Hello there, General Kenobi!
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:
fun1
,然后将返回值存储在$?
中。虽然我不建议这样做……fun1 || fun2 will only run fun2 if fun1 returns a 0 value.
不应该是a non-0 value
吗?||
构造,退出代码 0 被视为成功,因此为“真”。非零是错误,因此是错误的。将fun1 || fun2
视为“如果 fun1 返回成功或 fun2 返回成功”的简写,而不是实际返回值本身的运算符。