在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
你不能传递一个数组,你只能传递它的元素(即扩展的数组)。
另见:
-
[我关于这个话题的另一个答案]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[*]
要用""括起来,否则会失败。
【讨论】:
您的示例不正确,因为它不完整。请给出完整的脚本代码。 “片段”仅适用于 html 和 javascript。使用
按钮以代码突出显示其他语言的代码。【参考方案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)
descTable
和 optsTable
作为名称传递并在函数中展开。因此,当作为参数给出时,不需要$
。
请注意,即使 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】:这里的基本问题是设计/实现数组的 bash 开发人员真的把狗搞砸了。他们认为$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[@]
行发送:
-
这实际上是创建
descTable
和optsTable
数组的副本,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
不是必需的。
注意: 值得一提的是,使用这种括号形式的数组初始化会根据IFS
或 Internal Field Separator 来初始化新数组,默认情况下 tab、换行和空格。在这种情况下,由于它使用了[@]
表示法,因此每个元素都被视为自己被引用(与[*]
相反)。
我对它的预订
在BASH
中,局部变量作用域是当前函数和从它调用的每个子函数,这意味着takes_ary_as_arg()
函数“看到”那些descTable[@]
和optsTable[@]
数组,因此它正在工作(见上面的解释)。
既然如此,为什么不直接查看这些变量本身呢?就像写在那里一样:
argAry1=("$descTable[@]")
见上面的解释,它只是根据当前的IFS
复制descTable[@]
数组的值。
总结
从本质上讲,这并没有按价值传递——像往常一样。
我还想强调丹尼斯威廉姆森上面的评论:sparse 数组(没有定义所有键的数组 - 其中有“孔”)将无法按预期工作 - 我们会松开键和“压缩”数组。
话虽如此,我确实看到了泛化的价值,因此函数可以在不知道名称的情况下获取数组(或副本):
对于~“副本”:这种技术已经足够好了,只需要注意索引(键)已经消失了。对于真实副本: 我们可以对键使用 eval,例如:
eval local keys=(\$!$1)
然后循环使用它们来创建副本。
注意:这里!
没有使用它之前的间接/双重评估,而是在数组上下文中它返回数组索引(键)。
descTable
和optsTable
字符串(没有[@]
),我们可以将数组本身(如引用)与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中将数组作为参数传递的主要内容,如果未能解决你的问题,请参考以下文章