我有很多关联数组,但我只想使用 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
没有多维数组。但是,您可以使用关联数组和键串联(key1
并key2
表示为组合字符串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[@]}"