在 bash 的 key/value 对中迭代作为值嵌入的列表

Iterate over lists embedded as values in key/value pairs in bash

我正在尝试在 bash 中获取一个(键,多值)结构(某种哈希图),如下所示:

[   
  [ "abc" : 1, 2, 3, 4 ],
  [ "def" : "w", 33, 2 ]    
]

我想遍历每个键(某种 for key in ...,并使用 map["def",2]map[$key,2] 之类的东西获取每个值。

我看到几个线程在谈论单值哈希图,但没有提到这个问题。

我可以使用 N 数组,N 是我地图中键的数量,连续填充每个字段,但我不想重复代码可能。

提前致谢!

编辑: 我想通过这样的结构来完成:

for key in ${map[@]} do;
  echo $key # "abc" then "def"
  for value in ${map[$key,@]} do;
    ...
  done
done

可行。声明有点难看

declare -A map=(
    [abc,0]=1
    [abc,1]=2
    [abc,2]=3
    [abc,3]=4
    [def,0]=w
    [def,1]=33
    [def,2]=2
)
key="def"
i=1
echo "${map[$key,$i]}"   # => 33

迭代:有助于保持 "keys":

的单独数组
keys=(abc def)

然后

for key in "${keys[@]}"; do 
    echo "$key"
    for idx in "${!map[@]}"; do 
        if [[ $idx == $key,* ]]; then 
            n=${idx##*,}
            printf "\t%s\t%s\n" "$n" "${map["$idx"]}"
        fi
    done
done
abc
    0   1
    1   2
    2   3
    3   4
def
    1   33
    0   w
    2   2

在多数组情况下使用现代 bash 特征:


作业(手动):

map_abc=( 1 2 3 4 )
map_def=( w 33 2 )

作业(程序化):

append() {
  local array_name="_"; shift; shift
  declare -g -a "$array_name"
  declare -n array="$array_name" # BASH 4.3 FEATURE
  array+=( "$@" )
}
append map abc 1 2 3 4
append map def w 33 2

迭代(在函数内部完成以包含 namevar 的范围):

iter() {
  for array in ${!map_@}; do
    echo "Iterating over array ${array#map_}"
    declare -n cur_array="$array" # BASH 4.3 FEATURE
    for key in "${!cur_array[@]}"; do
      echo "$key: ${cur_array[$key]}"
    done
  done
}
iter

这也可以在没有名称变量的情况下完成,但方式更丑陋且更容易出错。 (需要明确的是,我相信此处给出的代码可以安全地使用 eval,但很容易出错——如果尝试在此模板上构建您自己的实现,请非常谨慎)。

# Compatible with older bash (should be through 3.x).
append() {
  local array_name="_"; shift; shift
  declare -g -a "$array_name"

  local args_str cmd_str
  printf -v args_str '%q ' "$@"
  printf -v cmd_str "%q+=( %s )" "$array_name" "$args_str"
  eval "$cmd_str"
}

...并且,以与 bash 兼容的方式迭代回到 3.x:

for array in ${!map_@}; do
  echo "Iterating over array ${array#map_}"

  printf -v cur_array_cmd 'cur_array=( ${%q[@]} )' "$array"
  eval "$cur_array_cmd"

  for key in "${!cur_array[@]}"; do
    echo "$key: ${cur_array[$key]}"
  done
done

这比通过单个大数组过滤(给出的另一个答案)在计算上更有效 -- 而且,当 namevars 可用时,可以说也会产生更清晰的代码。