代码之家  ›  专栏  ›  技术社区  ›  Wang

shell脚本中返回多个值的习惯用法

  •  19
  • Wang  · 技术社区  · 15 年前

    有没有从脚本中的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}"
    

    我意识到如果我需要在shell脚本中重新进入,我可能做得不对,但是我仍然觉得仅仅为了保存返回值而抛出全局变量是非常不舒服的。

    有更好的办法吗?我更喜欢可移植性,但如果必须指定 #!/bin/bash .

    9 回复  |  直到 7 年前
        1
  •  7
  •   cjds    15 年前

    尽管我非常喜欢shell,但很可能是这样的:只要您在周围抛出任意的结构化数据,unix bourne/posix shell就不是正确的选择。

    如果字段中没有出现字符,请用其中一个字符分隔。典型的例子是 /etc/passwd , /etc/group 以及使用冒号作为字段分隔符的各种其他文件。

    如果使用一个可以处理字符串中nul字符的shell,那么在nul上连接和分离(通过$ifs或其他方式)可以很好地工作。但是一些常见的炮弹,包括bash,在核弹上破裂。一个测试会很老的。我的信号是:

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

    即使这对您有用,您也已经达到了一个警告信号,即是时候切换到更结构化的语言(python、perl、ruby、lua、javascript……选择你喜欢的毒药)。您的代码很可能变得难以维护;即使您可以,也会有一小部分人能够很好地理解它并对其进行维护。

        2
  •  11
  •   mivk    8 年前

    在特殊情况下 您的值从不包含空格 ,这个 read 诀窍很简单:

    get_vars () {
      #...
      echo "value1" "value2"
    }
    
    read var1 var2 < <(get_vars)
    echo "var1='$var1', var2='$var2'"
    

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

        3
  •  9
  •   fronthem    7 年前

    这个问题是5年前发布的,但我有一些有趣的回答。我刚开始学bash,我也遇到了和你一样的问题。我认为这个技巧可能会有帮助:

    #!/bin/sh
    
    foo=""
    bar=""
    
    my_func(){
        echo 'foo="a"; bar="b"'
    }
    
    eval $(my_func)
    echo $foo $bar
    # result: a b
    

    当子进程无法将值发送回其父进程时,此技巧对于解决问题也很有用。

        4
  •  2
  •   Justin    9 年前

    为了不支持的bash版本 纳默夫 (在bash 4.3-alpha中引入)我可以定义helper函数,在该函数中,返回值被分配给给定的变量。有点像用 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
  •  1
  •   ghostdog74    15 年前

    使用bash 4 eg可以使用关联数组

    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"
    
        6
  •  1
  •   Markarian451    12 年前

    我会寻求解决办法 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
    }
    
        7
  •  1
  •   Justin    9 年前

    bash的更高版本支持 纳默夫 . 使用 declare -n var_name 给予 var_name 这个 纳默夫 属性。 纳默夫 为您的函数提供在C++函数中常用的“通过引用”返回多个值的能力。根据bash man page:

    变量可以分配给 纳默夫 属性使用 -N 选择 声明 地方的 用于创建 纳默夫 ,或对另一个变量的引用。这允许间接操纵变量。每当 纳默夫 变量被引用或分配给,操作实际上是在 纳默夫 变量的值。一 纳默夫 通常在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'
    
        8
  •  1
  •   NuSkooler    7 年前

    还有另一种方式:

    function get_tuple()
    {
      echo -e "Value1\nValue2"
    }
    
    IFS=$'\n' read -d '' -ra VALUES < <(get_tuple)
    echo "${VALUES[0]}" # Value1
    echo "${VALUES[1]}" # Value2
    
        9
  •  0
  •   sud03r    15 年前

    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
    

    这可能会减少您的不适,尽管它增加了创建新外壳的开销,因此会稍微慢一些。