给出存储在索引数组中的键值列表,其中每个键和值都是单独的元素:
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
" )'
$
是否有更简单或更简洁的方法来完成此操作?
代替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