在bash中将键值列表转换为关联数组的最佳代码模式是什么?

问题描述 投票:0回答:1

给出存储在索引数组中的键值列表,其中每个键和值都是单独的元素:

list=(
'key$1' "value 1 line 1
value 1 line 2
"
'key$2' $'another\nmulti-line\nvalue\n'
)

以下代码将正确地循环键值对并创建和关联数组:

declare -A arr
for (( i=0; i<${#list[*]}; i+=2 )); do
    arr+=( ["${list[$i]}"]="${list[$((i+1))]}" );
done
$ declare -p arr
declare -A arr='([key2]="another
multi-line
value
" [key1]="value 1 line 1
value 1 line 2
" )'
$ 

是否有更简单或更简洁的方法来完成此操作?

arrays bash associative-array
1个回答
2
投票

代替arr+=([key]=value),您可以写arr[key]=value。另外,$(())内不需要变量的$list[…]前缀。

declare -A arr
for (( i=0; i<"${#list[*]}"; i+=2 )); do
    arr["${list[i]}"]="${list[i+1]}"
done

除此之外,脚本似乎还不错。我认为唯一正确的选择是创建一个命令字符串,然后创建一个eval / declare。以下命令假定list中至少有一个键/值对:

declare -A "arr=($(printf '[%q]=%q ' "${list[@]}"))"

declare命令应该是安全的。同样,ARG_MAX应该不会有任何问题,因为仅使用了内置功能。但是,循环似乎快了一个数量级,请参阅以下基准。可以使用实际数据随意对这两种方法进行基准测试(仅在处理非常长的数组时才值得)。

randList() {
    # tr is necessary since the empty string cannot be used as a key
    mapfile -d '' -n "$1" list < <(tr -s \\0 < /dev/urandom)
}
testFor() { declare -A arr; for (( i=0; i<"${#list[*]}"; i+=2 )); do arr["${list[i]}"]="${list[i+1]}"; done; }
testDeclare() { declare -A "arr=($(printf '[%q]=%q ' "${list[@]}"))"; }
prettyTime() { { time "$@"; } 2>&1 | grep -Eom1 '[0-9.sm]+'; }
for size in {1,10,50}000; do
    randList "$size"
    echo "list size = $size"
    printf %s "for loop        "; prettyTime testFor
    printf %s "declare command "; prettyTime testDeclare
done

在我的笔记本电脑上(bash 5,intel i5 M 520),我得到了这些结果:

list size = 1000
for loop        0m0.059s
declare command 0m0.320s
list size = 10000
for loop        0m0.435s
declare command 0m2.395s
list size = 50000
for loop        0m2.540s
declare command 0m12.276s
© www.soinside.com 2019 - 2024. All rights reserved.