如何将数组作为参数传递给bash函数?
注意:在Stack Overflow上找不到答案后,我自己发布了一个有点粗糙的解决方案。它只允许传递一个数组,并且它是参数列表的最后一个元素。实际上,它根本没有传递数组,而是传递数组元素的列表,这些元素被called_function()重新组装成一个数组,但它对我来说是有效的。如果有人知道更好的方法,请在这里添加。
如何将数组作为参数传递给bash函数?
注意:在Stack Overflow上找不到答案后,我自己发布了一个有点粗糙的解决方案。它只允许传递一个数组,并且它是参数列表的最后一个元素。实际上,它根本没有传递数组,而是传递数组元素的列表,这些元素被called_function()重新组装成一个数组,但它对我来说是有效的。如果有人知道更好的方法,请在这里添加。
当前回答
DevSolar的回答有一点我不理解(也许他有特定的理由这么做,但我想不出一个):他根据位置参数一个元素一个元素地迭代设置数组。
一个更简单的解释是
called_function()
{
...
# do everything like shown by DevSolar
...
# now get a copy of the positional parameters
local_array=("$@")
...
}
其他回答
你也可以用数组创建一个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
您可以在这个类似的问题中找到更多的想法:如何将数组作为参数传递给Bash中的函数
我的简短回答是:
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[*]}应该被""包围,否则就会失败。
尽管它很丑,但这里有一个变通方法,只要你没有显式地传递一个数组,而是一个对应于数组的变量:
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[@]})”
希望这能帮助到一些人。
评论Ken Bertelson的解决方案并回答Jan Hettich:
它是如何工作的
try_with_local_arys()函数中的takes_ary_as_arg descTable[@] optsTable[@]行发送:
This is actually creates a copy of the descTable and optsTable arrays which are accessible to the takes_ary_as_arg function. takes_ary_as_arg() function receives descTable[@] and optsTable[@] as strings, that means $1 == descTable[@] and $2 == optsTable[@]. in the beginning of takes_ary_as_arg() function it uses ${!parameter} syntax, which is called indirect reference or sometimes double referenced, this means that instead of using $1's value, we use the value of the expanded value of $1, example: baba=booba variable=baba echo ${variable} # baba echo ${!variable} # booba likewise for $2. putting this in argAry1=("${!1}") creates argAry1 as an array (the brackets following =) with the expanded descTable[@], just like writing there argAry1=("${descTable[@]}") directly. the declare there is not required.
注意:值得一提的是,使用这个括号形式的数组初始化根据IFS或内部字段分隔符初始化新数组,默认为制表符、换行符和空格。在这种情况下,因为它使用[@]符号,所以每个元素都被看作是被引用的(与[*]相反)。
我对它的预订
在BASH中,局部变量作用域是当前函数和从它调用的每个子函数,这转化为这样一个事实,takes_ary_as_arg()函数“看到”那些descTable[@]和optsTable[@]数组,因此它是工作的(见上述解释)。
既然如此,为什么不直接看看这些变量本身呢?这就像写在这里:
argAry1=("${descTable[@]}")
参见上面的解释,它只是根据当前的IFS复制descTable[@]数组的值。
总之
从本质上讲,这只是过去,没有任何价值——和往常一样。
我还想强调Dennis Williamson上面的评论:稀疏数组(没有所有键定义的数组-其中有“孔”)不会像预期的那样工作-我们会失去键并“压缩”数组。
话虽如此,我确实看到了泛化的价值,因此函数可以在不知道名称的情况下获得数组(或副本):
对于~“拷贝”:这个技术已经足够好了,只是需要注意,索引(键)已经没有了。 对于真实副本: 我们可以对键使用eval,例如: Eval本地键=(\${!$1})
然后循环使用它们来创建副本。 注:这里!不是使用它之前的间接/双重求值,而是在数组上下文中返回数组下标(键)。
当然,如果我们要传递descTable和optsTable字符串(不带[@]),我们可以在eval中使用数组本身(如通过引用)。用于接受数组的泛型函数。
通过一些技巧,您实际上可以将命名参数与数组一起传递给函数。
我开发的方法允许你访问传递给函数的参数,就像这样:
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'