30

スクリプト内の bash 関数から複数の値を返すイディオムはありますか?

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

シェル スクリプトで再入可能性が必要な場合は、おそらく間違っていることを認識していますが、戻り値を保持するためだけにグローバル変数を投げることには、まだ非常に不快感を覚えます。

より良い方法はありますか?移植性を優先しますが、指定する必要がある場合、おそらく実際の制限ではありません#!/bin/bash

4

10 に答える 10

23

値にスペースが含まれないという特殊なケースでは、このreadトリックが簡単な解決策になります。

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

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

しかし、もちろん、値の 1 つにスペースがあるとすぐに壊れます。IFS関数の で特別なセパレーターを変更して使用することもできますechoが、その結果は、他の提案されたソリューションよりも実際には単純ではありません。

于 2016-08-21T10:54:33.943 に答える
11

私はシェルが大好きですが、おそらく、任意の構造化データを投げかけるとすぐに、Unix bourne/posix シェルは適切な選択ではないでしょう。

フィールド内に出現しない文字がある場合は、それらのいずれかで区切ります。古典的な例は/etc/passwd、 、/etc/groupおよびフィールド区切りとしてコロンを使用する他のさまざまなファイルです。

文字列内の NUL 文字を処理できるシェルを使用している場合、NUL での結合と分離 ($IFS などを介して) はうまく機能します。ただし、bash を含むいくつかの一般的なシェルは、NUL で壊れます。テストは、私の古い .sig になります。

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

それがうまくいくとしても、より構造化された言語 (Python、Perl、Ruby、Lua、Javascript ... 好みの毒を選んでください) に切り替える時期であるという警告サインの 1 つに到達しただけです。コードの保守が難しくなる可能性があります。たとえそれができたとしても、それを維持するのに十分なほど十分に理解している人々の少数のプールがあります.

于 2010-03-22T04:06:37.583 に答える
2

nameref (Bash 4.3-alpha で導入) をサポートしない Bash のバージョンでは、戻り値が指定された変数に割り当てられるヘルパー関数を定義できます。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
于 2016-07-16T19:13:31.157 に答える
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"
于 2010-03-22T00:34:26.263 に答える
1

Bash の新しいバージョンではnamerefがサポートされています。nameref属性declare -n var_nameを指定するためvar_nameに使用します。namerefは、複数の値を返すために C++ 関数で一般的に使用される「参照渡し」機能を関数に提供します。Bashのmanページによると:

-nオプションをdeclareまたはlocal組み込みコマンドに使用してnameref属性を変数に割り当て、namerefまたは別の変数への参照を作成できます。これにより、変数を間接的に操作できます。nameref変数が参照または代入されるときは常に、nameref変数の値によって指定された変数に対して操作が実際に実行されます。namerefは、一般にシェル関数内で使用され、名前が関数に引数として渡される変数を参照します。

以下は、インタラクティブなコマンド ラインの例です。

例 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'
于 2016-07-09T17:16:07.530 に答える
1

ここで提案したソリューションを使用しますが、代わりに配列変数を使用します。古い 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
}
于 2013-01-27T16:44:09.987 に答える
0

シェル スクリプト関数は、最後に実行されたコマンドの終了ステータス、または return ステートメントで明示的に指定されたその関数の終了ステータスのみを返すことができます。

文字列を返すには、次のようにします。

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

これにより不快感は軽減されるかもしれませんが、新しいシェルを作成するオーバーヘッドが追加されるため、わずかに遅くなります。

于 2010-03-21T21:34:23.010 に答える