在 bash 中将键值列表转换为关联数组的最佳代码模式是什么?
What is the most optimal code pattern for converting a key value list to associative array in bash?
给定一个存储在索引数组中的键值列表,其中每个键和值都是单独的元素:
list=(
'key' "value 1 line 1
value 1 line 2
"
'key' $'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 "" 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.0.16,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
给定一个存储在索引数组中的键值列表,其中每个键和值都是单独的元素:
list=(
'key' "value 1 line 1
value 1 line 2
"
'key' $'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 "" 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.0.16,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