57

如何将关联数组作为参数传递给函数?这在 Bash 中可行吗?

下面的代码没有按预期工作:

function iterateArray
{
    local ADATA="${@}"            # associative array

for key in "${!ADATA[@]}"
do
    echo "key - ${key}"
    echo "value: ${ADATA[$key]}"

done

}

将关联数组传递给像普通数组这样的函数不起作用:

iterateArray "$A_DATA"

或者

iterateArray "$A_DATA[@]"
4

9 回答 9

51

上周我遇到了完全相同的问题,并考虑了很长时间。

看来,关联数组不能被序列化或复制。关联数组有一个很好的 Bash FAQ 条目,其中详细解释了它们。最后一节给了我以下对我有用的想法:

function print_array {
    # eval string into a new associative array
    eval "declare -A func_assoc_array="${1#*=}
    # proof that array was successfully created
    declare -p func_assoc_array
}

# declare an associative array
declare -A assoc_array=(["key1"]="value1" ["key2"]="value2")
# show associative array definition
declare -p assoc_array

# pass associative array in string form to function
print_array "$(declare -p assoc_array)" 
于 2012-01-16T11:35:17.573 回答
51

如果您使用的是 Bash 4.3 或更高版本,最简洁的方法是按名称传递关联数组,然后使用带有local -n. 例如:

function foo {
    local -n data_ref=$1
    echo ${data_ref[a]} ${data_ref[b]}
}

declare -A data
data[a]="Fred Flintstone"
data[b]="Barney Rubble"
foo data

您不必使用_ref后缀;这正是我在这里挑选的。只要它与原始变量名称不同,您就可以随意调用该引用(否则您将收到“循环名称引用”错误)。

于 2019-03-14T19:19:24.630 回答
15

基于 Florian Feldhaus 的解决方案:

# Bash 4+ only
function printAssocArray # ( assocArrayName ) 
{
    var=$(declare -p "$1")
    eval "declare -A _arr="${var#*=}
    for k in "${!_arr[@]}"; do
        echo "$k: ${_arr[$k]}"
    done

}

declare -A conf
conf[pou]=789
conf[mail]="ab\npo"
conf[doo]=456

printAssocArray "conf" 

输出将是:

doo: 456
pou: 789
mail: ab\npo
于 2015-09-12T07:59:50.353 回答
9

更新,为了完全回答这个问题,这是我图书馆的一小部分:

通过引用迭代关联数组

shopt -s expand_aliases
alias array.getbyref='e="$( declare -p ${1} )"; eval "declare -A E=${e#*=}"'
alias array.foreach='array.keys ${1}; for key in "${KEYS[@]}"'

function array.print {
    array.getbyref
    array.foreach
    do
        echo "$key: ${E[$key]}"
    done
}

function array.keys {
    array.getbyref
    KEYS=(${!E[@]})
}   

# Example usage:
declare -A A=([one]=1 [two]=2 [three]=3)
array.print A

这是我早期工作的一个发展,我将在下面留下。

@ffeldhaus - 很好的回应,我拿着它跑了:

t() 
{
    e="$( declare -p $1 )"
    eval "declare -A E=${e#*=}"
    declare -p E
}

declare -A A='([a]="1" [b]="2" [c]="3" )'
echo -n original declaration:; declare -p A
echo -n running function tst: 
t A

# Output:
# original declaration:declare -A A='([a]="1" [b]="2" [c]="3" )'
# running function tst:declare -A E='([a]="1" [b]="2" [c]="3" )'
于 2012-06-10T02:51:38.717 回答
4

您只能按名称传递关联数组。

按名称传递常规数组也更好(更有效)。

于 2011-01-11T11:36:09.770 回答
3

哟:

 #!/bin/bash
   declare -A dict

   dict=(
    [ke]="va"
    [ys]="lu"
    [ye]="es" 
   )

   fun() {
     for i in $@; do
       echo $i
     done
    }

   fun ${dict[@]} # || ${dict[key]} || ${!dict[@] || ${dict[$1]} 

eZ

于 2017-09-06T23:21:59.800 回答
2

这是我今天eval echo ...提出的用于执行间接的解决方案:

print_assoc_array() {
    local arr_keys="\${!$1[@]}" # \$ means we only substitute the $1
    local arr_val="\${$1[\"\$k\"]}"
    for k in $(eval echo $arr_keys); do #use eval echo to do the next substitution
        printf "%s: %s\n" "$k" "$(eval echo $arr_val)"
    done
}

declare -A my_arr
my_arr[abc]="123"
my_arr[def]="456"
print_assoc_array my_arr

bash 4.3 上的输出:

def: 456
abc: 123
于 2016-06-24T18:55:12.263 回答
1

这是另一种方式:您可以在将关联数组传递给函数时手动序列化它,然后将其反序列化回函数内的新关联数组:

1.关联数组的手动序列化/反序列化

这是来自我的eRCaGuy_hello_world 存储库的完整、可运行的示例:

array_pass_as_bash_parameter_2_associative.sh

# Print an associative array using manual serialization/deserialization
# Usage:
#       # General form:
#       print_associative_array array_length array_keys array_values
#       # Example
#       print_associative_array "${#array1[@]}" "${!array1[@]}" "${array1[@]}"
print_associative_array() {
    i=1

    # read 1st argument, the array length
    array_len="${@:$i:1}"
    ((i++))

    # read all key:value pairs into a new associative array
    declare -A array
    for (( i_key="$i"; i_key<$(($i + "$array_len")); i_key++ )); do
        i_value=$(($i_key + $array_len))
        key="${@:$i_key:1}"
        value="${@:$i_value:1}"
        array["$key"]="$value"
    done

    # print the array by iterating through all of the keys now
    for key in "${!array[@]}"; do
        value="${array["$key"]}"
        echo "  $key: $value"
    done
}

# Let's create and load up an associative array and print it
declare -A array1
array1["a"]="cat"
array1["b"]="dog"
array1["c"]="mouse"

#                         length         indices (keys)    values
print_associative_array "${#array1[@]}" "${!array1[@]}" "${array1[@]}"

样本输出:

  a: cat
  b: dog
  c: mouse

解释:

对于名为 的给定函数print_associative_array,以下是一般形式:

# general form
print_associative_array array_length array_keys array_values

对于名为 的数组array1,以下是获取数组长度、索引(键)和值的方法:

  1. 数组长度:"${#array1[@]}"
  2. 所有数组索引(在本例中为键,因为它是关联数组):"${!array1[@]}"
  3. 所有数组值:"${array1[@]}"

因此,一个示例调用print_associative_array如下所示:

# example call
#                         length         indices (keys)    values
print_associative_array "${#array1[@]}" "${!array1[@]}" "${array1[@]}"

将数组的长度放在首位是必不可少的,因为它允许我们在传入的序列化数组到达所有传入参数print_associative_array的魔术数组中的函数时对其进行解析。@

要解析@数组,我们将使用数组切片,如下所述(此片段是从我的答案复制粘贴在这里):

# array slicing basic format 1: grab a certain length starting at a certain
# index
echo "${@:2:5}"
#         │ │
#         │ └────> slice length
#         └──────> slice starting index (zero-based)

2. [比上面更好的技术!] 通过引用传递数组

...正如@Todd Lehman 在他的回答中解释的那样

# Print an associative array by passing the array by reference
# Usage:
#       # General form:
#       print_associative_array2 array
#       # Example
#       print_associative_array2 array1
print_associative_array2() {
    # declare a local **reference variable** (hence `-n`) named `array_reference`
    # which is a reference to the value stored in the first parameter
    # passed in
    local -n array_reference="$1"

    # print the array by iterating through all of the keys now
    for key in "${!array_reference[@]}"; do
        value="${array_reference["$key"]}"
        echo "  $key: $value"
    done
}

echo 'print_associative_array2 array1'
print_associative_array2 array1
echo ""
echo "OR (same thing--quotes don't matter in this case):"
echo 'print_associative_array2 "array1"'
print_associative_array2 "array1"

样本输出:

print_associative_array2 array1
  a: cat
  b: dog
  c: mouse

OR (same thing--quotes don't matter in this case):
print_associative_array2 "array1"
  a: cat
  b: dog
  c: mouse

也可以看看:

  1. [我的回答]我序列化/反序列化常规“索引”bash数组以便将其中一个或多个作为参数传递给函数的更广泛的演示:Passing arrays as parameters in bash
  2. [我的回答] 我通过引用传递常规“索引”bash 数组的演示:Passing arrays as parameters in bash
  3. [我的答案] 数组切片:Unix & Linux:Bash:位置参数切片
  4. [我的问题]为什么man bash页面声明declareandlocal -n属性“不能应用于数组变量”,但它可以?
于 2022-02-10T06:56:00.247 回答
-1

来自有史以来最好的Bash 指南

declare -A fullNames
fullNames=( ["lhunath"]="Maarten Billemont" ["greycat"]="Greg Wooledge" )
for user in "${!fullNames[@]}"
do
    echo "User: $user, full name: ${fullNames[$user]}."
done

认为您的问题$@不是关联数组:“@:扩展为所有位置参数的所有单词。如果双引号,它将扩展为所有位置参数的列表作为单个单词。”

于 2011-09-05T11:51:21.590 回答