我正在创建关联数组以在for循环中进行处理,但是我以索引顺序得到了一些奇怪的结果。请看一下这个示例脚本:
#!/bin/bash
declare -A test1=(
[d]=1w45
[e]=2dfg
[m]=3df
[o]=4df
)
declare -A test2=(
[d1]=1w45
[e2]=2dfg
[m3]=3df
[o4]=4df
)
declare -A test3=(
[1d]=1w45
[2e]=2dfg
[3m]=3df
[4o]=4df
)
echo ${!test1[@]}
echo ${!test2[@]}
echo ${!test3[@]}
输出将是
$ ./test
d e m o
o4 m3 e2 d1
3m 4o 1d 2e
为什么项目顺序改变?以及如何绕过这种行为?预先感谢!
为什么不bash关联数组保持索引顺序?
因为它们被设计为不这样做。
为什么项目顺序改变?
Bash associative array implementation使用hash library并存储索引的哈希。哈希存储在buckets中,default number of buckets为128。使用简单的乘法和异或在hash_string()中计算哈希。键列在in the order buckets appear中。 Bucket number is calculated通过二进制&
和-将带有桶数减少的键的哈希值减1。
我编译了bash commit 6c6454cb18d7cd30b3b26d5ba6479431e599f3ed,对我来说,您的脚本输出是:
$ ./test
o m e d
d1 e2 m3 o4
1d 3m 2e 4o
所以我复制了`hash_string()函数并编写了一个小型C程序,该程序将输出键的存储区编号并进行编译和执行:
#include <stdio.h>
#define FNV_OFFSET 2166136261
#define FNV_PRIME 16777619
unsigned int
hash_string (s)
const char *s;
{
register unsigned int i;
for (i = FNV_OFFSET; *s; s++)
{
i *= FNV_PRIME;
i ^= *s;
}
return i;
}
int main() {
const char *s[] = {
"o", "m", "e", "d",
"d1", "e2", "m3", "o4",
"1d", "3m", "2e", "4",
};
for (int i = 0; i < sizeof(s)/sizeof(*s); ++i) {
printf("%3s %3d\n",
s[i],
hash_string(s[i]) & (128 - 1));
}
}
程序输出两列-键和键的存储区号(添加了多余的空行:]]
o 112 m 114 e 122 d 123 d1 16 e2 60 m3 69 o4 100 1d 14 3m 41 2e 50 4o 94
输出的键的顺序使用它们在哈希表中存储桶的顺序进行排序,因此它们按该顺序输出。这就是项目顺序更改的原因。
也就是说,您应该不是
依赖于此行为,因为如果bash的作者决定更改哈希函数或进行任何其他更改,则键的输出顺序可能会更改。以及如何绕过此行为?
没有办法绕过这个。 Bash数组使用哈希表存储哈希。密钥的插入顺序未存储在任何地方。
当然,您可以通过修补bash
来实现您要求的功能来绕过此行为。
也就是说,我只使用两个数组:
keys=(d1 e2 m3 o4) elements=(1w45 2dfg 3df 4df) declare -A test2 for ((i=0;i<${#keys[@]};++i)); do test2[${keys[$i]}]="${elements[$i]}" done # or maybe something along: declare -A test2=($(paste -zd <(printf "[%s]=\0" "${keys[@]}") <(printf "%q \0" "${elements[@]}"))
这样,您就可以按照将键插入到单独的
keys
数组中的顺序来遍历键。
根据评论,可以这样做来绕过此行为。
为什么项目顺序改变?