如何将数组作为参数传递给bash函数?

注意:在Stack Overflow上找不到答案后,我自己发布了一个有点粗糙的解决方案。它只允许传递一个数组,并且它是参数列表的最后一个元素。实际上,它根本没有传递数组,而是传递数组元素的列表,这些元素被called_function()重新组装成一个数组,但它对我来说是有效的。如果有人知道更好的方法,请在这里添加。


当前回答

你可以像这样传递多个数组作为参数:

takes_ary_as_arg()
{
    declare -a argAry1=("${!1}")
    echo "${argAry1[@]}"

    declare -a argAry2=("${!2}")
    echo "${argAry2[@]}"
}
try_with_local_arys()
{
    # array variables could have local scope
    local descTable=(
        "sli4-iread"
        "sli4-iwrite"
        "sli3-iread"
        "sli3-iwrite"
    )
    local optsTable=(
        "--msix  --iread"
        "--msix  --iwrite"
        "--msi   --iread"
        "--msi   --iwrite"
    )
    takes_ary_as_arg descTable[@] optsTable[@]
}
try_with_local_arys

将回声:

sli4-iread sli4-iwrite sli3-iread sli3-iwrite  
--msix  --iread --msix  --iwrite --msi   --iread --msi   --iwrite

编辑/注释:(摘自下面的评论)

descTable和optsTable作为名称传递,并在函数中展开。因此,在作为参数给出时不需要$。 注意,即使用local定义了descTable等,这仍然有效,因为locals对于它们调用的函数是可见的。 !在$ {!1}展开arg 1变量。 声明-a只是使索引数组显式,这不是严格必要的。

其他回答

你也可以用数组创建一个json文件,然后用jq解析这个json文件

例如:

my-array.json:

{
  "array": ["item1","item2"]
}

script.sh:

ARRAY=$(jq -r '."array"' $1 | tr -d '[],"')

然后像这样调用脚本:

script.sh ./path-to-json/my-array.json

您可以在这个类似的问题中找到更多的想法:如何将数组作为参数传递给Bash中的函数

通过一些技巧,您实际上可以将命名参数与数组一起传递给函数。

我开发的方法允许你访问传递给函数的参数,就像这样:

testPassingParams() {

    @var hello
    l=4 @array anArrayWithFourElements
    l=2 @array anotherArrayWithTwo
    @var anotherSingle
    @reference table   # references only work in bash >=4.3
    @params anArrayOfVariedSize

    test "$hello" = "$1" && echo correct
    #
    test "${anArrayWithFourElements[0]}" = "$2" && echo correct
    test "${anArrayWithFourElements[1]}" = "$3" && echo correct
    test "${anArrayWithFourElements[2]}" = "$4" && echo correct
    # etc...
    #
    test "${anotherArrayWithTwo[0]}" = "$6" && echo correct
    test "${anotherArrayWithTwo[1]}" = "$7" && echo correct
    #
    test "$anotherSingle" = "$8" && echo correct
    #
    test "${table[test]}" = "works"
    table[inside]="adding a new value"
    #
    # I'm using * just in this example:
    test "${anArrayOfVariedSize[*]}" = "${*:10}" && echo correct
}

fourElements=( a1 a2 "a3 with spaces" a4 )
twoElements=( b1 b2 )
declare -A assocArray
assocArray[test]="works"

testPassingParams "first" "${fourElements[@]}" "${twoElements[@]}" "single with spaces" assocArray "and more... " "even more..."

test "${assocArray[inside]}" = "adding a new value"

换句话说,您不仅可以通过名称调用参数(这使核心更具可读性),还可以实际传递数组(以及对变量的引用——该特性仅在bash 4.3中有效)!另外,映射的变量都在局部作用域中,就像$1(和其他变量)一样。

实现这一功能的代码非常简单,并且可以在bash 3和bash 4中工作(这是我测试过的唯一版本)。如果你对更多这样的技巧感兴趣,可以让bash开发变得更好更简单,你可以看看我的bash Infinity Framework,下面的代码就是为此目的而开发的。

Function.AssignParamLocally() {
    local commandWithArgs=( $1 )
    local command="${commandWithArgs[0]}"

    shift

    if [[ "$command" == "trap" || "$command" == "l="* || "$command" == "_type="* ]]
    then
        paramNo+=-1
        return 0
    fi

    if [[ "$command" != "local" ]]
    then
        assignNormalCodeStarted=true
    fi

    local varDeclaration="${commandWithArgs[1]}"
    if [[ $varDeclaration == '-n' ]]
    then
        varDeclaration="${commandWithArgs[2]}"
    fi
    local varName="${varDeclaration%%=*}"

    # var value is only important if making an object later on from it
    local varValue="${varDeclaration#*=}"

    if [[ ! -z $assignVarType ]]
    then
        local previousParamNo=$(expr $paramNo - 1)

        if [[ "$assignVarType" == "array" ]]
        then
            # passing array:
            execute="$assignVarName=( \"\${@:$previousParamNo:$assignArrLength}\" )"
            eval "$execute"
            paramNo+=$(expr $assignArrLength - 1)

            unset assignArrLength
        elif [[ "$assignVarType" == "params" ]]
        then
            execute="$assignVarName=( \"\${@:$previousParamNo}\" )"
            eval "$execute"
        elif [[ "$assignVarType" == "reference" ]]
        then
            execute="$assignVarName=\"\$$previousParamNo\""
            eval "$execute"
        elif [[ ! -z "${!previousParamNo}" ]]
        then
            execute="$assignVarName=\"\$$previousParamNo\""
            eval "$execute"
        fi
    fi

    assignVarType="$__capture_type"
    assignVarName="$varName"
    assignArrLength="$__capture_arrLength"
}

Function.CaptureParams() {
    __capture_type="$_type"
    __capture_arrLength="$l"
}

alias @trapAssign='Function.CaptureParams; trap "declare -i \"paramNo+=1\"; Function.AssignParamLocally \"\$BASH_COMMAND\" \"\$@\"; [[ \$assignNormalCodeStarted = true ]] && trap - DEBUG && unset assignVarType && unset assignVarName && unset assignNormalCodeStarted && unset paramNo" DEBUG; '
alias @param='@trapAssign local'
alias @reference='_type=reference @trapAssign local -n'
alias @var='_type=var @param'
alias @params='_type=params @param'
alias @array='_type=array @param'

注:这是我在Stack Overflow上找不到答案后自己发布的粗略的解决方案。它只允许传递一个数组,并且它是参数列表的最后一个元素。实际上,它根本没有传递数组,而是传递数组元素的列表,这些元素被called_function()重新组装成一个数组,但它对我来说是有效的。不久之后,Ken发布了他的解决方案,但我把我的保留在这里作为“历史”参考。

calling_function()
{
    variable="a"
    array=( "x", "y", "z" )
    called_function "${variable}" "${array[@]}"
}

called_function()
{
    local_variable="${1}"
    shift
    local_array=("${@}")
}

DevSolar的回答有一点我不理解(也许他有特定的理由这么做,但我想不出一个):他根据位置参数一个元素一个元素地迭代设置数组。

一个更简单的解释是

called_function()
{
  ...
  # do everything like shown by DevSolar
  ...

  # now get a copy of the positional parameters
  local_array=("$@")
  ...
}

传递几个数组作为参数的一个简单方法是使用字符分隔的字符串。你可以这样调用你的脚本:

./myScript.sh "value1;value2;value3" "somethingElse" "value4;value5" "anotherOne"

然后,你可以像这样在你的代码中提取它:

myArray=$1
IFS=';' read -a myArray <<< "$myArray"

myOtherArray=$3
IFS=';' read -a myOtherArray <<< "$myOtherArray"

通过这种方式,你实际上可以传递多个数组作为参数,它不必是最后一个参数。