2010-03-21 45 views
16

是否有任何用于从脚本中的bash函数返回多个值的成语?用于在shell脚本中返回多个值的成语

http://tldp.org/LDP/abs/html/assortedtips.html介绍如何呼应多个值和处理结果(例如,例如35-17),但得到棘手如果一些返回的值是用空格字符串英寸

更结构化的方式返回将分配给全局变量,像

foo() { 
    FOO_RV1="bob" 
    FOO_RV2="bill" 
} 

foo 
echo "foo returned ${FOO_RV1} and ${FOO_RV2}" 

我意识到,如果我需要重入的shell脚本我可能做错了,但我还是觉得很不舒服围绕刚刚举行扔全局变量返回值。

有没有更好的方法?我宁愿可移植性,但如果我必须指定#!/bin/bash,它可能不是一个真正的限制。

回答

6

虽然我很喜欢的外壳,它一旦这可能的情况下,你身边扔任意结构化数据,UNIX伯恩/ posix shell并不是正确的选择。

如果字段中不存在字符,则将其与其中一个分开。典型的例子是/etc/passwd,/etc/group和其他各种使用冒号作为字段分隔符的文件。

如果使用可以在字符串内部处理NUL字符的shell,那么加入NUL并在其上分隔(通过$ IFS或其他)可以很好地工作。但是几个常见的炮弹,包括bash,都打破了NUL。测试将是我的一个老的.sig:

foo=$'a\0b'; [ ${#foo} -eq 3 ] && echo "$0 rocks" 

即使这会为你工作,你刚刚达到的警示标志,它的时间切换到一个更有条理的语言中的一个(Python和Perl的,Ruby,Lua,Javascript ...选择你喜欢的毒药)。你的代码可能很难维护;即使可以,也有一小部分人能够很好地理解并维护它。

0

Shell脚本函数只能返回最后执行的命令的退出状态或返回语句显式指定的那个函数的退出状态。

要返回一些字符串的一种方式可能是这样的:

function fun() 
{ 
    echo "a+b" 
} 

var=`fun` # Invoke the function in a new child shell and capture the results 
echo $var # use the stored result 

这可以减少你的不适,虽然它增加了建立一个新的外壳的开销,因此会略低。

1

,您可以利用关联数组与你的bash 4例如

declare -A ARR 
function foo(){ 
    ... 
    ARR["foo_return_value_1"]="VAR1" 
    ARR["foo_return_value_2"]="VAR2" 
} 

,你可以将它们合并为字符串。

function foo(){ 
    ... 
    echo "$var1|$var2|$var3" 
} 

那么当你需要使用这些返回值,

ret="$(foo)" 
IFS="|" 
set -- $ret 
echo "var1 one is: $1" 
echo "var2 one is: $2" 
echo "var3 one is: $3" 
1

我会去的解决方案我suggested here,但使用数组变量来代替。较旧的bash:es不支持关联数组。 例如,

function some_func() # ARRVAR args... 
{ 
    local _retvar=$1 # I use underscore to avoid clashes with return variable names 
    local -a _out 
    # ... some processing ... (_out[2]=xxx etc.) 
    eval $_retvar='("${_out[@]}")' 
} 

调用网站:

function caller() 
{ 
    local -a tuple_ret # Do not use leading '_' here. 
    # ... 
    some_func tuple_ret "arg1" 
    printf " %s\n" "${tuple_ret[@]}" # Print tuple members on separate lines 
} 
5

这个问题在5年前发布的,但我有一些有趣的答案。我自己刚开始学习bash,而且我也遇到了和你一样的问题。我觉得这招可能会有所帮助:

#!/bin/sh 

foo="" 
bar="" 

my_func(){ 
    echo 'foo="a"; bar="b"' 
} 

print_result(){ 
    echo $1 $2 
} 

eval $(my_func) 
print_result $foo $bar 
# result: a b 

在你不想使用太多的全局变量的情况下,试试这个:

#!/bin/sh 

my_func(){ 
    echo 'print_it "a" "b"' 
} 

print_result(){ 
    echo $1 $2 
} 

eval $(my_func) 
# result: a b 

这招也是解决问题的有用的子进程无法将值发回父进程的全局变量,例如

#!/bin/sh 

msg="" #global variable 
stat="" 

say_hello(){ 
    msg="hello" # doesn't work at all! 
    echo "success" 
} 

output=$(say_hello) # child process $(...) return "success" 

解决方式:

#!/bin/sh 

msg="" #global variable 
stat="" 

say_hello(){ 
    echo 'msg="hello"; stat"success"' 
} 

eval $(say_hello) # this line evaluates to msg="hello"; stat"success" 
1

猛砸后来版本支持nameref。使用declare -n var_name给予var_namenameref属性。 nameref为您的函数提供了“通过引用传递”的能力,这是C++函数中常用的返回多个值的函数。根据击手册页:

变量可以被分配使用-n选项将声明本地内置命令来创建一个nameref,或参考的nameref属性到另一个变量。这允许间接操纵变量。每当变量被引用或分配给该变量时,该操作实际上都在由变量的值指定的变量上执行。 A nameref通常在shell函数内用来引用一个变量,该变量的名字作为参数传递给函数。

以下是一些交互式命令行示例。

实施例1:

$ unset xx yy 
$ xx=16 
$ yy=xx 
$ echo "[$xx] [$yy]" 
[16] [xx] 
$ declare -n yy 
$ echo "[$xx] [$yy]" 
[16] [16] 
$ xx=80 
$ echo "[$xx] [$yy]" 
[80] [80] 
$ yy=2016 
$ echo "[$xx] [$yy]" 
[2016] [2016] 
$ declare +n yy # Use -n to add and +n to remove nameref attribute. 
$ echo "[$xx] [$yy]" 
[2016] [xx] 

实施例2:

$ func() 
> { 
>  local arg1="$1" arg2="$2" 
>  local -n arg3ref="$3" arg4ref="$4" 
> 
>  echo '' 
>  echo 'Local variables:' 
>  echo " arg1='$arg1'" 
>  echo " arg2='$arg2'" 
>  echo " arg3ref='$arg3ref'" 
>  echo " arg4ref='$arg4ref'" 
>  echo '' 
> 
>  arg1='1st value of local assignment' 
>  arg2='2st value of local assignment' 
>  arg3ref='1st return value' 
>  arg4ref='2nd return value' 
> } 
$ 
$ unset foo bar baz qux 
$ 
$ foo='value of foo' 
$ bar='value of bar' 
$ baz='value of baz' 
$ qux='value of qux' 
$ 
$ func foo bar baz qux 

Local variables: 
    arg1='foo' 
    arg2='bar' 
    arg3ref='value of baz' 
    arg4ref='value of qux' 

$ 
$ { 
>  echo '' 
>  echo '2 values are returned after the function call:' 
>  echo " foo='$foo'" 
>  echo " bar='$bar'" 
>  echo " baz='$baz'" 
>  echo " qux='$qux'" 
> } 

2 values are returned after the function call: 
    foo='value of foo' 
    bar='value of bar' 
    baz='1st return value' 
    qux='2nd return value' 
1

在bash的顺序版本不支持nameref(在击引入4.3-α)予可以定义帮助函数,其中返回值被分配给给定的变量。这就像使用eval来做同样的变量赋值。

实施例1

## Add two complex numbers and returns it. 
## re: real part, im: imaginary part. 
## 
## Helper function named by the 5th positional parameter 
## have to have been defined before the function is called. 
complexAdd() 
{ 
    local re1="$1" im1="$2" re2="$3" im2="$4" fnName="$5" sumRe sumIm 

    sumRe=$(($re1 + $re2)) 
    sumIm=$(($im1 + $im2)) 

    ## Call the function and return 2 values. 
    "$fnName" "$sumRe" "$sumIm" 
} 

main() 
{ 
    local fooRe='101' fooIm='37' barRe='55' barIm='123' bazRe bazIm quxRe quxIm 

    ## Define the function to receive mutiple return values 
    ## before calling complexAdd(). 
    retValAssign() { bazRe="$1"; bazIm="$2"; } 
    ## Call comlexAdd() for the first time. 
    complexAdd "$fooRe" "$fooIm" "$barRe" "$barIm" 'retValAssign' 

    ## Redefine the function to receive mutiple return values. 
    retValAssign() { quxRe="$1"; quxIm="$2"; } 
    ## Call comlexAdd() for the second time. 
    complexAdd "$barRe" "$barIm" "$bazRe" "$bazIm" 'retValAssign' 

    echo "foo = $fooRe + $fooIm i" 
    echo "bar = $barRe + $barIm i" 
    echo "baz = foo + bar = $bazRe + $bazIm i" 
    echo "qux = bar + baz = $quxRe + $quxIm i" 
} 

main 

实施例2

## Add two complex numbers and returns it. 
## re: real part, im: imaginary part. 
## 
## Helper functions 
##  getRetRe(), getRetIm(), setRetRe() and setRetIm() 
## have to have been defined before the function is called. 
complexAdd() 
{ 
    local re1="$1" im1="$2" re2="$3" im2="$4" 

    setRetRe "$re1" 
    setRetRe $(($(getRetRe) + $re2)) 

    setRetIm $(($im1 + $im2)) 
} 

main() 
{ 
    local fooRe='101' fooIm='37' barRe='55' barIm='123' bazRe bazIm quxRe quxIm 

    ## Define getter and setter functions before calling complexAdd(). 
    getRetRe() { echo "$bazRe"; } 
    getRetIm() { echo "$bazIm"; } 
    setRetRe() { bazRe="$1"; } 
    setRetIm() { bazIm="$1"; } 
    ## Call comlexAdd() for the first time. 
    complexAdd "$fooRe" "$fooIm" "$barRe" "$barIm" 

    ## Redefine getter and setter functions. 
    getRetRe() { echo "$quxRe"; } 
    getRetIm() { echo "$quxIm"; } 
    setRetRe() { quxRe="$1"; } 
    setRetIm() { quxIm="$1"; } 
    ## Call comlexAdd() for the second time. 
    complexAdd "$barRe" "$barIm" "$bazRe" "$bazIm" 

    echo "foo = $fooRe + $fooIm i" 
    echo "bar = $barRe + $barIm i" 
    echo "baz = foo + bar = $bazRe + $bazIm i" 
    echo "qux = bar + baz = $quxRe + $quxIm i" 
} 

main 
5

您的值从不包含空格的特殊情况下,这read特技可以是一个简单的解决方案:

get_vars() { 
    #... 
    echo "value1" "value2" 
} 

read var1 var2 < <(get_vars) 
echo "var1='$var1', var2='$var2'" 

但当然,只要其中一个值有空格,它就会中断。您可以修改IFS并在函数的echo中使用特殊分隔符,但结果并不比其他建议的解决方案简单。

1

还有一种方法:

function get_tuple() 
{ 
    echo -e "Value1\nValue2" 
} 

IFS=$'\n' read -d '' -ra VALUES < <(get_tuple) 
echo "${VALUES[0]}" # Value1 
echo "${VALUES[1]}" # Value2