如何将数组作为参数传递给bash函数?
注意:在Stack Overflow上找不到答案后,我自己发布了一个有点粗糙的解决方案。它只允许传递一个数组,并且它是参数列表的最后一个元素。实际上,它根本没有传递数组,而是传递数组元素的列表,这些元素被called_function()重新组装成一个数组,但它对我来说是有效的。如果有人知道更好的方法,请在这里添加。
如何将数组作为参数传递给bash函数?
注意:在Stack Overflow上找不到答案后,我自己发布了一个有点粗糙的解决方案。它只允许传递一个数组,并且它是参数列表的最后一个元素。实际上,它根本没有传递数组,而是传递数组元素的列表,这些元素被called_function()重新组装成一个数组,但它对我来说是有效的。如果有人知道更好的方法,请在这里添加。
当前回答
只是添加到接受的答案,因为我发现如果数组内容是这样的,它就不能很好地工作:
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©
其他回答
这里的基本问题是,设计/实现数组的bash开发人员真的把事情搞砸了。他们认为${array}只是${array[0]}的简称,这是一个严重的错误。特别是当您认为${array[0]}没有意义并且如果数组类型是关联的,则计算结果为空字符串时。
Assigning an array takes the form array=(value1 ... valueN) where value has the syntax [subscript]=string, thereby assigning a value directly to a particular index in the array. This makes it so there can be two types of arrays, numerically indexed and hash indexed (called associative arrays in bash parlance). It also makes it so that you can create sparse numerically indexed arrays. Leaving off the [subscript]= part is short hand for a numerically indexed array, starting with the ordinal index of 0 and incrementing with each new value in the assignment statement.
因此,${array}应该计算为整个数组,索引和所有。它的求值应该是赋值语句的倒数。任何计算机科学专业的三年级学生都应该知道这一点。在这种情况下,这段代码将完全按照你所期望的那样工作:
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[*]}"
然后在函数内部写一堆代码来重新组装数组。
我的简短回答是:
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[*]}应该被""包围,否则就会失败。
你也可以用数组创建一个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中的函数
通过一些技巧,您实际上可以将命名参数与数组一起传递给函数。
我开发的方法允许你访问传递给函数的参数,就像这样:
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'
要求:在数组中查找字符串的函数。 这是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