Bash 和关联数组中的替换

Substition in Bash and associative array

我想动态声明和取消设置关联数组,但数组让我发疯,而且他们确实有最好的驾驶执照。 :-(

names=( Charlie Snoopy Linux Marcia )
intestines=$(printf "%s\n" ${names[@]} | awk '{ print "[""]="FNR  }' | tr "\n" " ")
echo $intestines # ok: [Charlie]=1 [Snoopy]=2 [Linux]=3 [Marcia]=4
unset namesAssociative
declare -A namesAssociative=( [Charlie]=1 [Snoopy]=2 [Linux]=3 [Marcia]=4 ) # works ok 
echo ${namesAssociative[Linux]} # OK: 3 

但是:

unset namesAssociative
declare -A namesAssociative=( $intestines ) # error
exec "declare -A namesAssociative=( $intestines )" # error
declare -A namesAssociative=( $(printf "%s\n" ${names[@]} | awk '{ print "[""]="FNR  }' | tr "\n" " ") ) # error
etc...

我想上帝在惩罚我,因为我没有从一开始就在 Python 中写...:-)

这正如您所期望的那样工作,并且使用关联数组键的 %q 格式指示器是安全的。

#!/usr/bin/env bash

names=( Charlie Snoopy Linux Marcia )

# shellcheck disable=SC2155 # Intended dynamic declaration
declare -A namesAssociative="($(
  for i in "${!names[@]}"; do
    printf '[%q]=%d ' "${names[i]}" $((i + 1))
  done
))"


declare -p namesAssociative

或者如果您的姓名数组不稀疏:

declare -A namesAssociative="($(
  i=1
  for k in "${names[@]}"; do
    printf '[%q]=%d ' "$k" $((i++))
  done
))"

更简单的演示,将关联数组名称 arr 放入列表中:

assoc_list="arr[first]=1 arr[second]=2 arr[third]=3"
unset arr
declare -A arr

eval $assoc_list

echo ${arr[second]}
2

另一个demo设置关联数组名稍后:

assoc_list="[first]=1 [second]=2 [third]=3"
# set associate array name as variable with value arr 
assoc_arr_name=arr
# create assoc_array_list from assoc_list
assoc_array_list=${assoc_list//[/$assoc_arr_name[}
echo $assoc_array_list
arr[first]=1 arr[second]=2 arr[third]=3


unset $assoc_arr_name
declare -A $assoc_arr_name

eval $assoc_array_list

echo ${arr[second]}
2

更一般的方式:

keys_arr=(first second third)
values_arr=(1 2 3)
map_name=assoc_arr
unset $map_name
declare -A $map_name
for ((i = 0 ; i < ${#keys_arr[@]} ; i++)); do 
    eval $map_name[${keys_arr[i]}]=${values_arr[i]};
done
echo ${assoc_arr[second]}
2

您过于坚持从动态字符串文字一次性创建数组,这取决于展开顺序的各种特性,可能需要 eval 等。

也许不要太用力,而是使用 for-cycle 代替:

declare -a names=( Charlie Snoopy Linux Marcia )
declare -Ai indices1 indices2  # to show two different options
declare -i index

for index in "${!names[@]}"; do
  indices1["${names[index]}"]=$((index + 1))      # option 1
  indices2+=(["${names[index]}"]=$((index + 1)))  # option 2
done

((${#indices1[@]} == ${#indices2[@]})) || echo heck blah

for name in "${!indices1[@]}"; do
  echo "${name}: $((indices1["${name}"])) $((indices2["${name}"]))"
done