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

正如我们所知,在 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

两个结果都不如我所愿。那么,有人知道如何正确地在函数之间传递数组吗?

104921 次浏览

You cannot pass an array, you can only pass its elements (i.e. the expanded array).

#!/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"

The other possibility would be to pass the array by name:

#!/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"

You could pass the "scalar" value first. That would simplify things:

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


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


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


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

At this point, you might as well use the array-ish positional params directly

f(){
b=$1
shift


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


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

This will solve the issue of passing array to function:

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

You can pass an array by name reference to a function in bash (since version 4.3+), by setting the -n attribute:

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

This works for indexed arrays:

$ shadock=(ga bu zo meu)
$ show_value shadock 2
zo

It also works for associative arrays:

$ declare -A days=([monday]=eggs [tuesday]=bread [sunday]=jam)
$ show_value days sunday
jam

See also nameref or declare -n in the man page.

Pass the array as a function

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

Try like this

function parseArray {
array=("$@")


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


array=("value" "value1")


parseArray "${array[@]}"

Here is an example where I receive 2 bash arrays into a function, as well as additional arguments after them. This pattern can be continued indefinitely for any number of bash arrays and any number of additional arguments, accommodating any input argument order, so long as the length of each bash array comes just before the elements of that array.

Function definition for 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[@]}"
}

Example usage:

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"

Example Output:

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

For further examples and detailed explanations of how this works, see my longer answer on this topic here: Passing arrays as parameters in bash

You can also create a json file with an array, and then parse that json file with jq

For example:

my-array.json:

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

script.sh:

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

And then call the script like:

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