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

Dou*_*dou 21 arrays bash loops

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

这是一个简单的脚本:

#!/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
Run Code Online (Sandbox Code Playgroud)

输出 :

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
Run Code Online (Sandbox Code Playgroud)

为什么我没有"group1","group2"等?

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

有办法吗?

谢谢 !

Jon*_*ler 31

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

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

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
Run Code Online (Sandbox Code Playgroud)

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

样本输出:

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
Run Code Online (Sandbox Code Playgroud)

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

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

  • 是否有任何理由不应该这样循环:`for i in“ $ {orders [@]}”; 回显“ $ i:$ {groups [$ i]}”;完成了吗? (3认同)

小智 5

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

master_array["group_list"]+="${new_group}";
Run Code Online (Sandbox Code Playgroud)

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

for group in ${master_array["group_list"]}; do
    echo "${group}";
    echo "${master_array[${group},destination_directory]}";
done
Run Code Online (Sandbox Code Playgroud)

这是该代码的输出:

"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"
Run Code Online (Sandbox Code Playgroud)

这类似于 Jonathan Leffler 的建议,但将数据与关联数组保留在一起,而不需要保留两个单独的不相交数组。如您所见,它不是按随机顺序,也不是按字母顺序,而是我将它们添加到数组中的顺序。

此外,如果您有子组,您可以为每个组创建子组列表,并通过这些列表进行排序。这就是我这样做的原因,以减轻对多个数组访问关联数组的需要,并允许扩展到新的子组而无需修改代码。

编辑:修正了一些错别字


小智 5

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

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