如何在Bash中保持关联数组顺序

时间:2020-12-20 00:02:13

I try to iterate over an associative array in Bash. It seems to be simple, but the loop doesn't follow the initial order of the array... I don't understand where is the mistake :/

我尝试在Bash中迭代一个关联数组。它似乎很简单,但循环不遵循数组的初始顺序......我不明白错误在哪里:/

Here is a simple script to try :

这是一个简单的尝试脚本:

#!/bin/bash

echo -e "Workspace\n----------";
lsb_release -a

echo -e "\nBash version\n----------";
echo -e $BASH_VERSION."\n";

declare -A groups;
groups["group1"]="123";
groups["group2"]="456";
groups["group3"]="789";
groups["group4"]="abc";
groups["group5"]="def";

echo -e "Result\n----------";
for i in "${!groups[@]}"
do
    echo "$i => ${groups[$i]}";
done

The output :

输出 :

Workspace
----------
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.2 LTS
Release:    14.04
Codename:   trusty

Bash version
----------
4.3.11(1)-release.

Result
----------
group3 => 789
group2 => 456
group1 => 123
group5 => def
group4 => abc

Why I don't have "group1", "group2", etc. ?

为什么我没有“group1”,“group2”等?

I don't want to have a alphanum order, I just want that the loop follow the initial declaration's order of the array...

我不想有一个alphanum命令,我只是想循环遵循数组的初始声明的顺序...

Is there a way ?

有办法吗?

Thanks !

谢谢 !

2 个解决方案

#1


22  

As already pointed out, there is no mistake. Associative arrays are stored in a 'hash' order. If you want ordering, you don't use associative arrays. Or, you use a non-associative array as well as an associative array.

正如已经指出的那样,没有错。关联数组以“哈希”顺序存储。如果要进行排序,则不要使用关联数组。或者,您使用非关联数组以及关联数组。

Keep a second (non-associative) array that identifies the keys in the order that they're created. Then step through the second array, using its contents to key the first (associative) array when printing the data. Like this:

保留第二个(非关联)数组,以按照创建顺序标识键。然后逐步执行第二个数组,使用其内容在打印数据时键入第一个(关联)数组。喜欢这个:

declare -A groups;      declare -a orders;
groups["group1"]="123"; orders+=( "group1" )
groups["group2"]="456"; orders+=( "group2" )
groups["group3"]="789"; orders+=( "group3" )
groups["group4"]="abc"; orders+=( "group4" )
groups["group5"]="def"; orders+=( "group5" )

# Convoluted option 1
for i in "${!orders[@]}"
do
    echo "${orders[$i]}: ${groups[${orders[$i]}]}"
done
echo

# Convoluted option 1 - 'explained'
for i in "${!orders[@]}"
do
    echo "$i: ${orders[$i]}: ${groups[${orders[$i]}]}"
done
echo

# Simpler option 2 - thanks, PesaThe
for i in "${orders[@]}"
do
    echo "$i: ${groups[$i]}"
done

The 'simpler option 2' was suggested by PesaThe in a comment, and should be used in preference to the 'convoluted option'.

PesaThe在评论中提出了“更简单的选项2”,应该优先使用'复杂选项'。

Sample output:

样本输出:

group1: 123
group2: 456
group3: 789
group4: abc
group5: def

0: group1: 123
1: group2: 456
2: group3: 789
3: group4: abc
4: group5: def

group1: 123
group2: 456
group3: 789
group4: abc
group5: def

You probably don't want to have two statements per line like that, but it emphasizes the parallelism between the handling of the two arrays.

您可能不希望每行有两个语句,但它强调了两个数组处理之间的并行性。

The semicolons after the assignments in the question are not really necessary (though they do no active harm, beyond leaving the reader wondering 'why?').

问题中的作业之后的分号并不是必需的(虽然它们没有主动伤害,除了让读者想知道'为什么?')。

#2


4  

Another way to sort entries in your associative array is to keep a list of the groups as you add them as an entry in the associative array. Call this entry key "group_list". As you add each new group, append it to the group_list field, adding a blank space to separate subsequent additions. Here's one I did for an associative array I called master_array:

对关联数组中的条目进行排序的另一种方法是在将组添加为关联数组中的条目时保留组的列表。将此条目键称为“group_list”。在添加每个新组时,将其附加到group_list字段,添加空格以分隔后续添加。这是我为一个叫做master_array的关联数组做的一个:

master_array["group_list"]+="${new_group}";

To sequence through the groups in the order you added them, sequence through the group_list field in a for loop, then you can access the group fields in the associative array. Here's a code snippet for one I wrote for master_array:

要按照添加它们的顺序对组进行排序,请按顺序遍历for循环中的group_list字段,然后可以访问关联数组中的组字段。这是我为master_array编写的代码片段:

for group in ${master_array["group_list"]}; do
    echo "${group}";
    echo "${master_array[${group},destination_directory]}";
done

and here's the output from that code:

这是该代码的输出:

"linux"
"${HOME}/Backup/home4"
"data"
"${HOME}/Backup/home4/data"
"pictures"
"${HOME}/Backup/home4/pictures"
"pictures-archive"
"${HOME}/Backup/home4/pictures-archive"
"music"
"${HOME}/Backup/home4/music"

This is similar to the suggestion by Jonathan Leffler, but keeps the data with the associative array rather than needing to keep two separate disjoint arrays. As you can see, it's not in random order, nor in alphabetical order, but the order in which I added them to the array.

这类似于Jonathan Leffler的建议,但是使用关联数组保存数据,而不是需要保留两个单独的不相交数组。正如您所看到的,它不是按随机顺序,也不是按字母顺序排列,而是按顺序添加到数组中。

Also, if you have subgroups, you can create subgroup lists for each group, and sequence through those as well. That's the reason I did it this way, to alleviate the need for multiple arrays to access the associative array, and also to allow for expansion to new subgroups without having to modify the code.

此外,如果您有子组,则可以为每个组创建子组列表,并按顺序排列。这就是我这样做的原因,减轻了多个数组访问关联数组的需要,并且允许扩展到新的子组而不必修改代码。

EDIT: fixed a few typos

编辑:修正了一些拼写错误

#1


22  

As already pointed out, there is no mistake. Associative arrays are stored in a 'hash' order. If you want ordering, you don't use associative arrays. Or, you use a non-associative array as well as an associative array.

正如已经指出的那样,没有错。关联数组以“哈希”顺序存储。如果要进行排序,则不要使用关联数组。或者,您使用非关联数组以及关联数组。

Keep a second (non-associative) array that identifies the keys in the order that they're created. Then step through the second array, using its contents to key the first (associative) array when printing the data. Like this:

保留第二个(非关联)数组,以按照创建顺序标识键。然后逐步执行第二个数组,使用其内容在打印数据时键入第一个(关联)数组。喜欢这个:

declare -A groups;      declare -a orders;
groups["group1"]="123"; orders+=( "group1" )
groups["group2"]="456"; orders+=( "group2" )
groups["group3"]="789"; orders+=( "group3" )
groups["group4"]="abc"; orders+=( "group4" )
groups["group5"]="def"; orders+=( "group5" )

# Convoluted option 1
for i in "${!orders[@]}"
do
    echo "${orders[$i]}: ${groups[${orders[$i]}]}"
done
echo

# Convoluted option 1 - 'explained'
for i in "${!orders[@]}"
do
    echo "$i: ${orders[$i]}: ${groups[${orders[$i]}]}"
done
echo

# Simpler option 2 - thanks, PesaThe
for i in "${orders[@]}"
do
    echo "$i: ${groups[$i]}"
done

The 'simpler option 2' was suggested by PesaThe in a comment, and should be used in preference to the 'convoluted option'.

PesaThe在评论中提出了“更简单的选项2”,应该优先使用'复杂选项'。

Sample output:

样本输出:

group1: 123
group2: 456
group3: 789
group4: abc
group5: def

0: group1: 123
1: group2: 456
2: group3: 789
3: group4: abc
4: group5: def

group1: 123
group2: 456
group3: 789
group4: abc
group5: def

You probably don't want to have two statements per line like that, but it emphasizes the parallelism between the handling of the two arrays.

您可能不希望每行有两个语句,但它强调了两个数组处理之间的并行性。

The semicolons after the assignments in the question are not really necessary (though they do no active harm, beyond leaving the reader wondering 'why?').

问题中的作业之后的分号并不是必需的(虽然它们没有主动伤害,除了让读者想知道'为什么?')。

#2


4  

Another way to sort entries in your associative array is to keep a list of the groups as you add them as an entry in the associative array. Call this entry key "group_list". As you add each new group, append it to the group_list field, adding a blank space to separate subsequent additions. Here's one I did for an associative array I called master_array:

对关联数组中的条目进行排序的另一种方法是在将组添加为关联数组中的条目时保留组的列表。将此条目键称为“group_list”。在添加每个新组时,将其附加到group_list字段,添加空格以分隔后续添加。这是我为一个叫做master_array的关联数组做的一个:

master_array["group_list"]+="${new_group}";

To sequence through the groups in the order you added them, sequence through the group_list field in a for loop, then you can access the group fields in the associative array. Here's a code snippet for one I wrote for master_array:

要按照添加它们的顺序对组进行排序,请按顺序遍历for循环中的group_list字段,然后可以访问关联数组中的组字段。这是我为master_array编写的代码片段:

for group in ${master_array["group_list"]}; do
    echo "${group}";
    echo "${master_array[${group},destination_directory]}";
done

and here's the output from that code:

这是该代码的输出:

"linux"
"${HOME}/Backup/home4"
"data"
"${HOME}/Backup/home4/data"
"pictures"
"${HOME}/Backup/home4/pictures"
"pictures-archive"
"${HOME}/Backup/home4/pictures-archive"
"music"
"${HOME}/Backup/home4/music"

This is similar to the suggestion by Jonathan Leffler, but keeps the data with the associative array rather than needing to keep two separate disjoint arrays. As you can see, it's not in random order, nor in alphabetical order, but the order in which I added them to the array.

这类似于Jonathan Leffler的建议,但是使用关联数组保存数据,而不是需要保留两个单独的不相交数组。正如您所看到的,它不是按随机顺序,也不是按字母顺序排列,而是按顺序添加到数组中。

Also, if you have subgroups, you can create subgroup lists for each group, and sequence through those as well. That's the reason I did it this way, to alleviate the need for multiple arrays to access the associative array, and also to allow for expansion to new subgroups without having to modify the code.

此外,如果您有子组,则可以为每个组创建子组列表,并按顺序排列。这就是我这样做的原因,减轻了多个数组访问关联数组的需要,并且允许扩展到新的子组而不必修改代码。

EDIT: fixed a few typos

编辑:修正了一些拼写错误