下面的答案向您展示了如何将 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