bash 如何保持关联数组的顺序?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29161323/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 12:34:33  来源:igfitidea点击:

How to keep associative array order?

arraysbashloops

提问by Doubidou

I try to iterate over an associative array in Bash.

我尝试在 Bash 中迭代关联数组。

It seems to be simple, but the loop doesn't follow the initial order of the array.

看起来很简单,但循环并不遵循数组的初始顺序。

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.?

为什么我没有group1group2等等?

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

我不想有字母顺序,我只希望循环遵循数组的初始声明顺序......

Is there a way?

有办法吗?

回答by Jonathan Leffler

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 PesaThein 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?').

问题中分配后的分号并不是真正必要的(尽管它们没有积极的危害,除了让读者想知道“为什么?”)。

回答by Randyman99

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 forloop, 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

编辑:修正了一些错别字

回答by Adi Degani

My approach is to create a sorted array of keys first:

我的方法是首先创建一个排序的键数组:

keys=( $( echo ${!dict[@]} | tr ' ' $'\n' | sort ) )
for k in ${keys[@]}; do
    echo "$k=${dict[$k]}"
done