在bash中将数组作为参数传递

Posted

技术标签:

【中文标题】在bash中将数组作为参数传递【英文标题】:Passing arrays as parameters in bash 【发布时间】:2010-11-06 23:51:18 【问题描述】:

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

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

【问题讨论】:

Here 你有很好的参考和大量的例子。 Err... 在同一分钟内对一个五年前的问题投了三票? 这是一个全面的答案:***.com/questions/6212219/… 相关:Passing parameters to a Bash function 参见:How to pass array as an argument to a function in Bash 【参考方案1】:

现代 bash (apparently version 4.3 or later),允许您通过引用传递数组。我将在下面展示。如果您想手动序列化和反序列化数组,请改用see my answer here for bash regular "indexed" arrays 和here for bash associative arrays。如下所示,通过引用传递数组更简单、更简洁,不过,这就是我现在推荐的方式。

下面的代码也可以在我的eRCaGuy_hello_world repo 中在线获得:array_pass_as_bash_parameter_by_reference.sh。另请参阅此处的示例:array_pass_as_bash_parameter_2_associative.sh。

这是常规 bash 数组的演示:

function foo 
    # declare a local **reference variable** (hence `-n`) named `data_ref`
    # which is a reference to the value stored in the first parameter
    # passed in
    local -n data_ref="$1"
    echo "$data_ref[0]"
    echo "$data_ref[1]"


# declare a regular bash "indexed" array
declare -a data
data+=("Fred Flintstone")
data+=("Barney Rubble")
foo "data"

样本输出:

Fred Flintstone
Barney Rubble

...这里是 关联 bash 数组的演示(即:bash 哈希表、“字典”或“无序映射”):

function foo 
    # declare a local **reference variable** (hence `-n`) named `data_ref`
    # which is a reference to the value stored in the first parameter
    # passed in
    local -n data_ref="$1"
    echo "$data_ref["a"]"
    echo "$data_ref["b"]"


# declare a bash associative array
declare -A data
data["a"]="Fred Flintstone"
data["b"]="Barney Rubble"
foo "data"

样本输出:

Fred Flintstone
Barney Rubble

参考资料:

    我从@Todd Lehman 的回答中修改了上述代码示例:How to pass an associative array as argument to a function in Bash? 另见my manual serializing/deserializing answer here 并在此处查看我的后续问题:Why do the man bash pages state the declare and local -n attribute "cannot be applied to array variables", and yet it can?

【讨论】:

【参考方案2】:

下面的答案向您展示了如何将 bash 常规“索引”数组作为参数传递给函数,主要是通过序列化和反序列化

    要查看本手册序列化/反序列化 bash 关联数组(哈希表)而不是常规索引数组,see my answer here。 要获得更好的方法(我认为需要 bash 版本 4.3 或更高版本)通过引用传递数组,请参阅上面的链接和my other answer here .
      通过引用传递数组更容易、更简洁,所以我现在建议这样做。话虽如此,我在下面展示的手动序列化/反序列化技术也非常有用。

快速总结: 请参阅下面的 3 个单独的函数定义。我复习一下如何通过:

    一个 bash 数组到一个函数 一个函数的两个或多个 bash 数组,以及 两个或多个 bash 数组加上函数的附加参数(在数组之前或之后)。

12 年后,我仍然没有在这里看到任何我真正喜欢的答案,我认为这些答案足够彻底、足够简单和“规范”,足以让我使用——我可以回来的答案一次又一次地复制和粘贴并在需要时展开。所以,这是我的答案,我认为是所有这些事情。

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

您也可以将其称为“bash 函数或脚本中的可变参数解析”,特别是因为传递给下面示例的每个数组中的元素数量可以动态变化,并且在 bash 中,数组的元素本质上是传递给即使通过像 "$array1[@]" 这样的单个数组扩展参数传入数组,该函数也可以作为单独的输入参数。

对于下面的所有示例代码,假设您有这两个 bash 数组进行测试:

array1=()
array1+=("one")
array1+=("two")
array1+=("three")

array2=("four" "five" "six" "seven" "eight")

上面和下面的代码可在我的bash/array_pass_as_bash_parameter.sh 文件中找到,该文件位于我在 GitHub 上的eRCaGuy_hello_world 存储库中。

示例 1:如何将一个 bash 数组传递给函数

要将数组传递给 bash 函数,您必须分别传递其所有元素。 给定 bash 数组array1,获取该数组所有元素的语法为"$array1[@]"。由于 bash 函数或可执行文件的所有传入参数都包含在名为 @ 的魔法 bash 输入参数数组中,因此您可以使用 "$@" 语法读取输入数组的所有成员,如下所示。

函数定义:

# Print all elements of a bash array.
# General form:
#       print_one_array array1
# Example usage:
#       print_one_array "$array1[@]"
print_one_array() 
    for element in "$@"; do
        printf "    %s\n" "$element"
    done

示例用法:

echo "Printing array1"
# This syntax passes all members of array1 as separate input arguments to 
# the function
print_one_array "$array1[@]"

示例输出:

Printing array1
    one
    two
    three

示例 2:如何将两个或多个 bash 数组传递给函数...

(以及如何再次将输入数组重新捕获为单独的 bash 数组)

这里,我们需要区分哪些传入的参数属于哪个数组。为此,我们需要知道每个数组的大小,即每个数组中元素的个数。这与在 C 中传递数组非常相似,我们通常还必须知道传递给任何 C 函数的数组长度。给定 bash 数组array1,其中的元素个数可以用"$#array1[@]" 获得(注意# 符号的用法)。为了知道array_len长度参数在输入参数中的位置,我们必须始终为每个数组传递数组长度参数传递单个数组之前元素,如下图。

为了解析数组,我在输入参数数组@上使用数组切片

这里提醒一下 bash 数组切片语法的工作原理(来自 my answer here)。在切片语法:start:length 中,第一个数字是开始切片的从零开始的索引,第二个数字是要抓取的元素数:

# array slicing basic format 1: grab a certain length starting at a certain
# index
echo "$@:2:5"
#         │ │
#         │ └────> slice length
#         └──────> slice starting index (zero-based)

# array slicing basic format 2: grab all remaining array elements starting at a
# certain index through to the end
echo "$@:2"
#         │
#         │
#         └──────> slice starting index (zero-based)

另外,为了强制将输入数组中的切片参数变成一个新数组,我将它们括在括号中(),就像这样,例如("$@:$i:$array1_len")。再一次,外面的括号很重要,因为这就是我们在 bash 中创建数组的方式。

下面的这个例子只接受两个 bash 数组,但是按照给定的模式,它可以很容易地接受 任意数量的 bash 数组作为参数。

函数定义:

# Print all elements of two bash arrays.
# General form (notice length MUST come before the array in order
# to be able to parse the args!):
#       print_two_arrays array1_len array1 array2_len array2
# Example usage:
#       print_two_arrays "$#array1[@]" "$array1[@]" \
#       "$#array2[@]" "$array2[@]"
print_two_arrays() 
    # For debugging: print all input args
    echo "All args to 'print_two_arrays':"
    print_one_array "$@"

    i=1

    # Read array1_len into a variable
    array1_len="$@:$i:1"
    ((i++))
    # Read array1 into a new array
    array1=("$@:$i:$array1_len")
    ((i += $array1_len))

    # Read array2_len into a variable
    array2_len="$@:$i:1"
    ((i++))
    # Read array2 into a new array
    array2=("$@:$i:$array2_len")
    ((i += $array2_len))

    # Print the two arrays
    echo "array1:"
    print_one_array "$array1[@]"
    echo "array2:"
    print_one_array "$array2[@]"

示例用法:

echo "Printing array1 and array2"
print_two_arrays "$#array1[@]" "$array1[@]" "$#array2[@]" "$array2[@]"

示例输出:

Printing array1 and array2
All args to 'print_two_arrays':
    3
    one
    two
    three
    5
    four
    five
    six
    seven
    eight
array1:
    one
    two
    three
array2:
    four
    five
    six
    seven
    eight

示例 3:将 两个 bash 数组加上之后的一些额外参数传递给一个函数

这是对上述示例的微小扩展。它还使用 bash 数组切片,就像上面的示例一样。然而,我们并没有在解析两个完整的输入数组后停止,而是在最后继续并解析更多的参数。对于任意数量的 bash 数组和任意数量的附加参数,只要每个 bash 数组的长度恰好位于该数组的元素之前,这种模式就可以无限期地继续下去,以适应任何输入参数顺序。

函数定义:

# Print all elements of two bash arrays, plus two extra args at the end.
# General form (notice length MUST come before the array in order
# to be able to parse the args!):
#       print_two_arrays_plus_extra_args array1_len array1 array2_len array2 \
#       extra_arg1 extra_arg2
# Example usage:
#       print_two_arrays_plus_extra_args "$#array1[@]" "$array1[@]" \
#       "$#array2[@]" "$array2[@]" "hello" "world"
print_two_arrays_plus_extra_args() 
    i=1

    # Read array1_len into a variable
    array1_len="$@:$i:1"
    ((i++))
    # Read array1 into a new array
    array1=("$@:$i:$array1_len")
    ((i += $array1_len))

    # Read array2_len into a variable
    array2_len="$@:$i:1"
    ((i++))
    # Read array2 into a new array
    array2=("$@:$i:$array2_len")
    ((i += $array2_len))

    # You can now read the extra arguments all at once and gather them into a
    # new array like this:
    extra_args_array=("$@:$i")

    # OR you can read the extra arguments individually into their own variables
    # one-by-one like this
    extra_arg1="$@:$i:1"
    ((i++))
    extra_arg2="$@:$i:1"
    ((i++))

    # Print the output
    echo "array1:"
    print_one_array "$array1[@]"
    echo "array2:"
    print_one_array "$array2[@]"
    echo "extra_arg1 = $extra_arg1"
    echo "extra_arg2 = $extra_arg2"
    echo "extra_args_array:"
    print_one_array "$extra_args_array[@]"

示例用法:

echo "Printing array1 and array2 plus some extra args"
print_two_arrays_plus_extra_args "$#array1[@]" "$array1[@]" \
"$#array2[@]" "$array2[@]" "hello" "world"

示例输出:

Printing array1 and array2 plus some extra args
array1:
    one
    two
    three
array2:
    four
    five
    six
    seven
    eight
extra_arg1 = hello
extra_arg2 = world
extra_args_array:
    hello
    world

参考资料:

    我在eRCaGuy_hello_world repo 中引用了很多我自己的示例代码:
      array_practice.sh array_slicing_demo.sh
    [我对 bash 数组切片的回答]Unix & Linux: Bash: slice of positional parameters An answer to my question on "How can I create and use a backup copy of all input args ("$@") in bash?" - 非常对于输入参数数组的一般数组操作很有用 An answer to "How to pass array as an argument to a function in Bash",这向我证实了这个非常重要的概念:

    你不能传递一个数组,你只能传递它的元素(即扩展的数组)。

另见:

    [我关于这个话题的另一个答案]How to pass array as an argument to a function in Bash

【讨论】:

【参考方案3】:

我的简短回答是:

function display_two_array 
    local arr1=$1
    local arr2=$2
    for i in $arr1
    do
       echo "arrary1: $i"
    done
    
    for i in $arr2
    do
       echo "arrary2: $i"
    done


test_array=(1 2 3 4 5)
test_array2=(7 8 9 10 11)

display_two_array "$test_array[*]" "$test_array2[*]"

注意$test_array[*]$test_array2[*]要用""括起来,否则会失败。

【讨论】:

您的示例不正确,因为它不完整。请给出完整的脚本代码。 “片段”仅适用于 htmljavascript。使用 按钮以代码突出显示其他语言的代码。【参考方案4】:

注意:这是我在 Stack Overflow 上找不到答案后自己发布的有点粗糙的解决方案。它只允许传递一个数组,并且它是参数列表的最后一个元素。实际上,它根本不是传递数组,而是传递其元素的列表,这些元素通过调用函数()重新组装成一个数组,但它对我有用。稍后,Ken 发布了他的解决方案,但我将我的解决方案保留在这里以供“历史”参考。

calling_function()

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


called_function()

    local_variable="$1"
    shift
    local_array=("$@")

【讨论】:

事后三年,这个答案 - 仅出于历史原因而保留 - 在几天内收到了两次反对票。可悲的是,在 SO 上通常如此,没有任何说明人们为什么认为这是有道理的。请注意,此答案早于所有其他答案,并且我接受了 Ken 的答案作为最佳解决方案。我完全清楚它远非完美,但四个 个月 它是 SO 上最好的。为什么它应该在肯的完美解决方案获得第二名之后两年被否决。 @geirha:我会请你检查谁发布了这个问题,谁发布了这个答案,以及谁可能接受你称之为“坏”的答案。 ;-) 您可能还想检查问题中的 Note,它指出了为什么此解决方案不如 Ken 的解决方案。 我知道你问了这个问题,你写了这个答案,并且你接受了错误的答案。这就是我这样说的原因。接受的答案不好的原因是它试图通过引用传递数组,这是你应该真正避免的。此外,该示例将多个参数混合到一个字符串中。如果您确实需要通过引用传递数组,那么 bash 是错误的语言开始。即使使用 bash 4.3 的新 nameref 变量,您也不能安全地避免名称冲突(循环引用)。 好吧,如果你包括每个数组的元素数量,你可以传递多个数组。 called_function "$#array[@]" "$array[@]" "$#array2[@]" "$array2[@]" 等等......仍然有一些明显的限制,但实际上,最好以语言支持的方式解决问题,而不是试图将语言弯曲成你习惯于使用其他语言的方式。 @geirha:好吧,我想我们必须同意我们不同意,你必须让我判断哪个答案最能回答我的问题。就个人而言,我更喜欢通过引用传递数组无论如何(无论语言如何,以保存数据复制);当替代方案是向后弯曲并将数组大小作为附加参数传递时更是如此......【参考方案5】:

您可以使用以下方式传递多个数组作为参数

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

编辑/注释:(来自下面的 cmets)

descTableoptsTable 作为名称传递并在函数中展开。因此,当作为参数给出时,不需要$。 请注意,即使 descTable 等被定义为 local,这仍然有效,因为局部变量对于它们调用的函数是可见的。 $!1 中的 ! 扩展了 arg 1 变量。 declare -a 只是使索引数组显式,并非绝对必要。

【讨论】:

需要注意的是,如果原始数组是稀疏的,那么接收函数中的数组将不会有相同的索引。 这太棒了,但是 Ken 或其他人能否解释一些令我困惑的事情: 1 - 我原以为 descTable 和 optsTable 在传递时必须以 $ 为前缀作为函数参数。 2 - 在“takes...”的第一行,为什么需要明确的数组声明? 3 - 那是什么!在表达式 $!1 中表示,为什么 [@] 不是必需的,甚至是不允许的? -- 这行得通,根据我的测试,似乎需要所有这些细节,但我想了解原因! 1:descTable 和 optsTable 只是作为名称传递,因此没有 $,它们应该只在被调用函数中扩展 2:不完全确定,但我认为这不是真的必要 3: !之所以使用,是因为传递给函数的参数需要扩展两次:$1 扩展为“descTable[@]”,应该扩展为“$descTable[@]”。 $!1 语法就是这样做的。 我认为“declare -a”部分是不必要的。括号的存在已经将赋值的 LHS 定义为一个数组。 这个回答帮我解决了刚才的一个问题。但是,我想指出,在我的机器上(使用 bash 4.3.42)“$!1”和“$!2”需要去掉引号。如果不这样做,则将原始数组的值读取为一个字符串并分别分配给argAry1[0]和argAry2[0],基本上意味着数组结构丢失了。【参考方案6】:

虽然丑陋,但只要您不显式传递数组,而是传递与数组对应的变量,这里有一个解决方法:

function passarray()

    eval array_internally=("$(echo '$'$1'[@]')")
    # access array now via array_internally
    echo "$array_internally[@]"
    #...


array=(0 1 2 3 4 5)
passarray array # echo's (0 1 2 3 4 5) as expected

我相信有人可以想出一个更清晰的想法实现,但我发现这比将数组作为"array[@]" 传递然后在内部使用array_inside=("$@") 访问它是一个更好的解决方案。当有其他位置/getopts 参数时,这会变得复杂。在这些情况下,我必须首先确定然后使用shift 和数组元素删除的某种组合来删除与数组无关的参数。

纯粹主义者的观点可能认为这种方法违反了语言,但从务实的角度来说,这种方法为我节省了很多痛苦。在一个相关主题上,我还使用eval 将内部构造的数组分配给根据参数命名的变量target_varname 我传递给函数:

eval $target_varname=$"($array_inside[@])"

希望这对某人有所帮助。

【讨论】:

【参考方案7】:

只是添加到接受的答案,因为我发现如果数组内容类似于:

RUN_COMMANDS=(
  "command1 param1... paramN"
  "command2 param1... paramN"
)

在这种情况下,数组的每个成员都会被拆分,所以函数看到的数组等价于:

RUN_COMMANDS=(
    "command1"
    "param1"
     ...
    "command2"
    ...
)

为了让这个案例工作,我发现的方法是将变量名传递给函数,然后使用eval:

function () 
    eval 'COMMANDS=( "$'"$1"'[@]" )'
    for COMMAND in "$COMMANDS[@]"; do
        echo $COMMAND
    done


function RUN_COMMANDS

只是我的 2©

【讨论】:

【参考方案8】:

这里的基本问题是设计/实现数组的 ba​​sh 开发人员真的把狗搞砸了。他们认为$array 只是$array[0] 的简写,这是一个严重的错误。尤其是当您认为$array[0] 没有意义并且如果数组类型是关联的,则计算结果为空字符串。

分配数组采用array=(value1 ... valueN) 的形式,其中value 的语法为[subscript]=string,从而将值直接分配给数组中的特定索引。这使得它可以有两种类型的数组,数字索引和哈希索引(在 bash 用语中称为关联数组)。它还使您可以创建稀疏的数字索引数组。省略 [subscript]= 部分是数字索引数组的简写,从序数索引 0 开始,并随着赋值语句中的每个新值递增。

因此,$array 应该评估为 整个 数组、索引和所有。它应该评估为赋值语句的逆。任何三年级的 CS 专业学生都应该知道这一点。在这种情况下,此代码将完全按照您的预期工作:

declare -A foo bar
foo=$bar

然后,将数组按值传递给函数并将一个数组分配给另一个数组将按照 shell 语法的其余部分进行。但是因为他们没有做到这一点,赋值运算符= 不适用于数组,并且数组不能按值传递给函数或子shell 或一般的输出(echo $array),无需代码咀嚼通过这一切。

所以,如果操作正确,那么下面的示例将显示 bash 中数组的实用性如何显着提高:

simple=(first=one second=2 third=3)
echo $simple

结果输出应该是:

(first=one second=2 third=3)

然后,数组可以使用赋值运算符,并通过值传递给函数甚至其他 shell 脚本。通过输出到文件轻松存储,并轻松从文件加载到脚本中。

declare -A foo
read foo <file

唉,我们被一个***的 bash 开发团队失望了。

因此,要将数组传递给函数,实际上只有一个选择,那就是使用 nameref 功能:

function funky() 
    local -n ARR

    ARR=$1
    echo "indexes: $!ARR[@]"
    echo "values: $ARR[@]"


declare -A HASH

HASH=([foo]=bar [zoom]=fast)
funky HASH # notice that I'm just passing the word 'HASH' to the function

将产生以下输出:

indexes: foo zoom
values: bar fast

由于这是通过引用传递的,因此您也可以在函数中分配给数组。是的,被引用的数组必须具有全局范围,但考虑到这是 shell 脚本,这应该没什么大不了的。要将关联或稀疏索引数组按值传递给函数,需要将所有索引和值作为单个字符串扔到参数列表中(如果它是一个大数组,则不太有用),如下所示:

funky "$!array[*]" "$array[*]"

然后在函数内部编写一堆代码来重组数组。

【讨论】:

使用local -n 的解决方案比公认的答案更好,更新。此解决方案也适用于任何类型的变量。此答案中列出的示例可以缩短为local -n ARR=$1。但是,local/declare-n 选项仅在 Bash 4.3 及更高版本中可用。 这很好!小问题:如果你传递一个与函数的本地参数同名的变量(例如funky ARR),shell 会给出警告circular name reference,因为基本上函数会尝试执行local -n ARR=ARR。好 discussion 关于这个话题。 阅读该页面后,我得出的结论是:1. 在 bash 中使用数组是一个非常好的想法 2. 如果您的想法是写一些优雅的东西,请使用 IFS 并避免在 bash 中使用数组。跨度> 【参考方案9】:

要求:在数组中查找字符串的函数。 这是对 DevSolar 解决方案的略微简化,因为它使用传递的参数而不是复制它们。

myarray=('foobar' 'foxbat')

function isInArray() 
  local item=$1
  shift
  for one in $@; do
    if [ $one = $item ]; then
      return 0   # found
    fi
  done
  return 1       # not found


var='foobar'
if isInArray $var $myarray[@]; then
  echo "$var found in array"
else
  echo "$var not found in array"
fi 

【讨论】:

【参考方案10】:

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

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

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

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

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

这样,您实际上可以将多个数组作为参数传递,而不必是最后一个参数。

【讨论】:

【参考方案11】:

评论 Ken Bertelson 解决方案并回答 Jan Hettich:

工作原理

try_with_local_arys() 函数中的takes_ary_as_arg descTable[@] optsTable[@] 行发送:

    这实际上是创建descTableoptsTable 数组的副本,takes_ary_as_arg 函数可以访问这些数组。 takes_ary_as_arg() 函数接收descTable[@]optsTable[@] 作为字符串,这意味着$1 == descTable[@]$2 == optsTable[@]

    takes_ary_as_arg()函数的开头它使用$!parameter语法,称为indirect reference or sometimes double referenced,这意味着不是使用$1的值,而是使用的值$1的扩展,例如:

    baba=booba
    variable=baba
    echo $variable # baba
    echo $!variable # booba
    

    $2 也是如此。

    把它放在argAry1=("$!1") 中创建argAry1 作为一个数组(= 后面的括号)和扩展的descTable[@],就像直接在那里写argAry1=("$descTable[@]") 一样。 declare 不是必需的。

注意: 值得一提的是,使用这种括号形式的数组初始化会根据IFSInternal Field Separator 来初始化新数组,默认情况下 tab换行空格。在这种情况下,由于它使用了[@] 表示法,因此每个元素都被视为自己被引用(与[*] 相反)。

我对它的预订

BASH 中,局部变量作用域是当前函数和从它调用的每个子函数,这意味着takes_ary_as_arg() 函数“看到”那些descTable[@]optsTable[@] 数组,因此它正在工作(见上面的解释)。

既然如此,为什么不直接查看这些变量本身呢?就像写在那里一样:

argAry1=("$descTable[@]")

见上面的解释,它只是根据当前的IFS复制descTable[@]数组的值。

总结

从本质上讲,这并没有按价值传递——像往常一样。

我还想强调丹尼斯威廉姆森上面的评论:sparse 数组(没有定义所有键的数组 - 其中有“孔”)将无法按预期工作 - 我们会松开键和“压缩”数组。

话虽如此,我确实看到了泛化的价值,因此函数可以在不知道名称的情况下获取数组(或副本):

对于~“副本”:这种技术已经足够好了,只需要注意索引(键)已经消失了。

对于真实副本: 我们可以对键使用 eval,例如:

eval local keys=(\$!$1)

然后循环使用它们来创建副本。 注意:这里! 没有使用它之前的间接/双重评估,而是在数组上下文中它返回数组索引(键)。

当然,如果我们要传递descTableoptsTable 字符串(没有[@]),我们可以将数组本身(如引用)与eval 一起使用。对于接受数组的通用函数。

【讨论】:

对 Ken Bertelson 解释背后的机制进行了很​​好的解释。对于“既然如此,为什么不直接查看这些变量本身?”的问题,我将回答:只是为了重用函数。假设我需要用Array1 调用一个函数,然后用Array2 调用,传递数组名称就很方便了。【参考方案12】:
function aecho 
  set "$1[$2]"
  echo "$!1"

例子

$ foo=(dog cat bird)

$ aecho foo 1
cat

【讨论】:

【参考方案13】:

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

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

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'

【讨论】:

【参考方案14】:

这个甚至可以使用空格:

format="\t%2s - %s\n"

function doAction

  local_array=("$@")
  for (( i = 0 ; i < $#local_array[@] ; i++ ))
    do
      printf "$format" $i "$local_array[$i]"
  done
  echo -n "Choose: "
  option=""
  read -n1 option
  echo $local_array[option]
  return


#the call:
doAction "$tools[@]"

【讨论】:

我想知道这里有什么意义。这只是正常的参数传递。 "$@" 语法适用于空格:"$@" 等价于 "$1" "$2"... 我可以将 2 个数组传递给一个函数吗?【参考方案15】:

DevSolar 的回答有一点我不明白(也许他有特定的原因,但我想不出一个):他从位置参数中逐个元素地设置数组,迭代。

一个更简单的方法是

called_function()

  ...
  # do everything like shown by DevSolar
  ...

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

【讨论】:

我不这样做的原因是直到几天前我还没有玩过 bash 数组。以前,如果 Perl 变得复杂,我会切换到它,这是我目前工作中没有的选择。感谢您的提示!

以上是关于在bash中将数组作为参数传递的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中将数组作为函数参数传递

如何在Java中将对象数组作为参数传递

我们可以在 PHP 中的任何函数中将数组作为参数传递吗?

在 JavaScript 中将数组作为函数参数传递

在golang中将数组作为参数传递

在 TypeScript 中将数组作为参数传递