如何在Bash中将数组作为参数传递给函数

114

众所周知,在bash编程中传递参数的方法是$1,...,$N。然而,我发现将数组作为参数传递给接收多个参数的函数并不容易。这是一个例子:

f(){
 x=($1)
 y=$2

 for i in "${x[@]}"
 do
  echo $i
 done
 ....
}

a=("jfaldsj jflajds" "LAST")
b=NOEFLDJF

f "${a[@]}" $b
f "${a[*]}" $b

如描述所述,函数f接收两个参数:第一个分配给x,它是一个数组,第二个分配给y

f可以通过两种方式调用。第一种方法使用"${a[@]}"作为第一个参数,结果为:

jfaldsj 
jflajds
第二种方法使用"${a[*]}"作为第一个参数,结果是:
jfaldsj 
jflajds 
LAST

结果都不如我所愿。那么,有没有人对如何正确地在函数之间传递数组有任何想法呢?


3
可能是在Bash中传递数组作为参数的重复问题。 - chepner
1
@chepner 谢谢提醒。我稍后会检查一下,看看有什么想法。 - Red Lv
https://dev59.com/BXNA5IYBdhLWcg3wL6oc - meso_2600
8个回答

130

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

#!/bin/bash
function f() {
    a=("$@")
    ((last_idx=${#a[@]} - 1))
    b=${a[last_idx]}
    unset a[last_idx]

    for i in "${a[@]}" ; do
        echo "$i"
    done
    echo "b: $b"
}

x=("one two" "LAST")
b='even more'

f "${x[@]}" "$b"
echo ===============
f "${x[*]}" "$b"

另一种可能性是通过名称传递数组:

#!/bin/bash
function f() {
    name=$1[@]
    b=$2
    a=("${!name}")

    for i in "${a[@]}" ; do
        echo "$i"
    done
    echo "b: $b"
}

x=("one two" "LAST")
b='even more'

f x "$b"

1
@RedLv:只需传递数组的名称,并使用!进行参数扩展即可获取该数组。 - choroba
13
请注意,“name”变量的值仅为字符串“x[@]”。魔法在${!...}中发生。 - glenn jackman
5
注意: 通过名称传递数组相当于通过引用传递。调用函数中所做的任何更改都会改变全局值。此外,函数内部的局部数组无法被传递。 - anishsane
1
@anishsane今天在类似的SO问题上发布了一个强大的方法,使用最近的nameref和另一种字段分隔符。链接 - F. Hauri - Give Up GitHub
1
我使用了“name”的技巧,因为这是我知道的唯一可行的方法。 - choroba
显示剩余12条评论

82

在Bash中(自版本4.3+起),您可以通过名称引用将数组传递给函数,方法是设置-n属性:

show_value () # array index
{
    local -n myarray=$1
    local idx=$2
    echo "${myarray[$idx]}"
}

这适用于索引数组:
$ shadock=(ga bu zo meu)
$ show_value shadock 2
zo

它也适用于关联数组:
$ declare -A days=([monday]=eggs [tuesday]=bread [sunday]=jam)
$ show_value days sunday
jam

请参考 man 页面中的 namerefdeclare -n


2
如果您的shell支持它,那么最佳答案是使用最轻量级的代码并且直截了当。谢谢:> - MrPotatoHead
1
缺点是你必须在函数中使用另一个数组名称进行调用。 - Edouard Thiel
1
由于CentOS 7 (centos-release-7-7.1908.0.el7.centos.x86_64)的默认bash版本为4.2.26,因此不支持此功能。 - Iridium Cao
1
show_value 函数中,myarrayarray 中可能有一个是拼写错误。它们应该使用相同的名称。 - mpb
看到了,已经修复。谢谢 :) - Edouard Thiel
显示剩余2条评论

19

你可以首先传递“标量”值。这将简化事情:

f(){
  b=$1
  shift
  a=("$@")

  for i in "${a[@]}"
  do
    echo $i
  done
  ....
}

a=("jfaldsj jflajds" "LAST")
b=NOEFLDJF

f "$b" "${a[@]}"

此时,您可以直接使用类似数组的位置参数。

f(){
  b=$1
  shift

  for i in "$@"   # or simply "for i; do"
  do
    echo $i
  done
  ....
}

f "$b" "${a[@]}"

1
你的想法在这种情况下是可行的。然而,它没有抓住将数组作为参数传递的要点。 - Red Lv
谢谢,这很好。我的服务器默认的bash shell版本低于4.3,你的解决方案可以。 - Iridium Cao
我喜欢这个因为它很容易理解。适用于4.3以下的版本。 - Paul Samsotha
这并不正确,我的评论已被删除,非常抱歉! - Dave
没关系,我也做过同样的事情很多次。 - glenn jackman

4

试试这样做

function parseArray {
    array=("$@")

    for data in "${array[@]}"
    do
        echo ${data}
    done
}

array=("value" "value1")

parseArray "${array[@]}"

这应该完全被标记为答案。 - undefined

4

这将解决传递数组到函数的问题:

#!/bin/bash

foo() {
    string=$1
    array=($@)
    echo "array is ${array[@]}"
    echo "array is ${array[1]}"
    return
}
array=( one two three )
foo ${array[@]}
colors=( red green blue )
foo ${colors[@]}

0
将数组作为函数传递
array() {
    echo "apple pear"
}

printArray() {
    local argArray="${1}"
    local array=($($argArray)) # where the magic happens. careful of the surrounding brackets.
    for arrElement in "${array[@]}"; do
        echo "${arrElement}"
    done

}

printArray array

0

你也可以创建一个包含数组的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

0

这里有一个例子,我将两个bash数组和它们后面的其他参数传递给一个函数。这种模式可以无限制地继续下去,适用于任意数量的bash数组任意数量的附加参数,并且可以容纳任何输入参数顺序,只要每个bash数组的长度刚好在该数组的元素之前。

print_two_arrays_plus_extra_args函数定义:

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

# 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[@]}"
}

使用示例:

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

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

echo "Printing array1 and array2 plus some extra args"
# Note that `"${#array1[@]}"` is the array length (number of elements
# in the array), and `"${array1[@]}"` is the array (all of the elements
# in the array) 
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

如需更多示例和详细说明,请参阅我在此主题上的更长回答:在Bash中将数组作为参数传递


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接