bash循环关联数组与变量数组名称

bash循环关联数组与变量数组名称

我有很多关联数组,但我只想使用 1 个循环。按给定名称选择 for 循环数组

我想用变量选择/构建数组名称的一部分,然后用该名称循环,但它不起作用。

与 OUTPUT3 和 OUTPUT4 类似,但语法错误。

对于输出 3,我收到:“bash 错误替换”

对于输出 4,我收到:“仅数组名和 0”

#!/bin/bash
clear

declare -A a1 a2 a3

a1['1']="1-1V"

a2['1']="2-1V"
a2['2']="2-2V"

a3['1']="3-1V"
a3['2']="3-2V"
a3['3']="3-3V"

# 1 OUTPUT WORKS
for i in ${!a1[*]}
do
echo -e "$i : ${a1[$i]}"
done

# 2 OUTPUT WORKS
for i in ${!a2[*]}
do
echo -e "$i : ${a2[$i]}"
done

# 3 OUTPUT - WRONG SYNTAX
selectkey="3"
for i in ${!a$selectkey[@]}
do
echo -e "$i : ${a$selectkey[$i]}"
done

# 4 OUTPUT - WRONG SYNTAX
key="3"
aselect="a${key}[*]"
# THIS ECHO WORKS
echo -e "ARRAY: ${!aselect}"

for i in ${!aselect[@]}
do
echo -e "$i : ${aselect[$i]}"
done

更新

解决办法是nameref|declare -n

这现在对我有用:

#!/bin/bash
clear

declare -A a1 a2 a3

a1['1']="1-1V"

a2['1']="2-1V"
a2['2']="2-2V"

a3['1']="3-1V"
a3['2']="3-2V"
a3['3']="3-3V"

varname="a3"
counter=1

declare -n refname=${varname}

for i in "${!refname[@]}"
do
echo -e "$counter ${refname[$counter]}"
counter=$((counter+1))
done

答案1

使用“nameref”:declare -n a=b使变量成为a变量的别名b

# 3 OUTPUT - use a "nameref"
selectkey="3"
declare -n ary="a$selectkey"

for i in "${!ary[@]}"
do
    echo "$i : ${ary[$i]}"
done

输出

3 : 3-3V
2 : 3-2V
1 : 3-1V

关联数组本质上是无序的。

对于#4,您正在使用“间接扩展”,如中所述外壳参数扩展,但是您无法使用该技术获取数组的索引。

答案2

bash没有多维数组。但是,您可以使用关联数组和键串联(key1key2表示为组合字符串key1,key2)来实现它们。这里有些例子。

鉴于您的起始场景,

declare -A a

a[1,1]='1-1V'
a[2,1]='2-1V' a[2,2]='2-2V'
a[3,1]='3-1V' a[3,2]='3-2V' a[3,3]='3-3V'

所有a[1,*]值:

for i in $(printf "%s\n" "${!a[@]}" | awk -F, -vk='1' '$1==k {print $2}')
do
    printf "%s: %s\n" "$i" "${a[1,$i]}"
done

输出

1: 1-1V

所有a[*,2]值:

for i in $(printf "%s\n" "${!a[@]}" | awk -F, -vk='2' '$2==k {print $1}')
do
    printf "%s: %s\n" "$i" "${a[$i,2]}"
done

输出

3: 3-2V
2: 2-2V

生成密钥集的另一种方法:

for i in $(printf "%s\n" "${!a[@]}" | awk -F, -vk='1' '$2==k')
do
    printf "%s: %s\n" "$i" "${a[$i]}";
done

输出

1,1: 1-1V
3,1: 3-1V
2,1: 2-1V

请注意,在所有情况下,键都不按数字顺序排列。如果需要,您可以修改printf | awk构造以通过管道sort -n(假设数字键)。

您还可以概括该awk构造,以便指定字段号以及所需的键值。这将有助于封装,也许可以通过将密钥生成隐藏在函数中

awk -F, -vfield=2 -vkey=1 '$field==key'

答案3

如果您的索引是数字,那么您最好使用常规数组而不是关联数组。两者都可以是稀疏的,但常规数组允许您按数字顺序迭代键,而对于关联数组来说,它本质上是随机的。 (在其他语言中,您可能需要关联数组才能获得稀疏数组。)比较:

$ declare -A a=([11]=a [22]=b [33]=c ) 
$ echo "${a[@]}"
b a c
$ declare -a b=([11]=a [22]=b [33]=c)
$ echo "${b[@]}"
a b c

再说一次,如果您的索引是数字并且数组不稀疏,您可以使用关联数组来伪造二维数组,并通过手动循环迭代索引。

declare -A a
a[1,1]="1-1V"

a[2,1]="2-1V"
a[2,2]="2-2V"

a[3,1]="3-1V"
a[3,2]="3-2V"
a[3,3]="3-3V"

x=3
i=1
while [[ ${a[$x,$i]+set} == set ]]; do
    echo "${a[$x,$i]}"
    ((i++))
done

(当然,您可以通过将“不存在”元素设置为某个特殊值来模拟稀疏数组,然后在循环内忽略该值。)

Ksh 实际上还支持二维数组,或者至少支持嵌套列表:

a[1][1]="1-1V"

a[2][1]="2-1V"
a[2][2]="2-2V"

a[3][1]="3-1V"
a[3][2]="3-2V"
a[3][3]="3-3V"

x=3
for x in "${a[x][@]}"; do
    echo "$x"
done

虽然这不起作用:

for x in "${a[@][1]}"; do
    echo "$x"
done

不过,需要复杂的数据结构也是考虑从 shell 切换到其他编程语言可能有用的情况之一。

在任何情况下,都不要使用${!array[*]}, 或${!array[@]}(带或不带!),而是"${!array[@]}"相反,因为这是唯一使包含空格的值保持完整的方法。尝试例如z=("foo bar" "zoom"),然后使用或for x in ${z[*]}; do echo $x; done与相同的进行比较。"${z[*]}""${z[@]}"

相关内容