2010-03-21 447 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