bash 如何在 shell 中创建 key:array 的映射?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41675740/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 22:29:06  来源:igfitidea点击:

How to create a map of key:array in shell?

arraysbashshellunix

提问by saurav

I want to create map in shell. Where each value is an array. So the map is key:array pair. For example it can be like this :

我想在 shell 中创建地图。其中每个值都是一个数组。所以地图是键:数组对。例如它可以是这样的:

"Key1" : a1 a2 a3 a4
"key2" : b1 b2 b3
"key3" : c1

basically my code looks like this

基本上我的代码看起来像这样

listService(){
serviceType=
servicesList=($(getServices $serviceType))
}

listService serviceTypeA
listService serviceTypeB
listService serviceTypeC

here getServicesis a function which returns an array of services based on the argument passed as $serviceType. So every time i call the listServicefunction my serviceListgets overridden by new service list. But I want to keep all the services from different service type in form of a map like this :

getServices是一个函数,它根据作为 传递的参数返回服务数组$serviceType。所以每次我调用这个listService函数时,我serviceList都会被新的服务列表覆盖。但我想以这样的地图形式保留来自不同服务类型的所有服务:

"serviceA" : a1 a2 a3 a4
"serviceB" : b1 b2 b3
"serviceC" : c1

After that I want to access each array based on the key. How to achieve this.

之后我想根据密钥访问每个数组。如何实现这一目标。

Thanks in advance for your help.

在此先感谢您的帮助。

Edit : I tried the answer provided by @cdarke . Here is my code now :

编辑:我尝试了@cdarke 提供的答案。这是我现在的代码:

#!/bin/bash
declare -A arrayMap

getValues(){
  key=
  case $key in
    AAA )
    arr=( AA AAA AAAA )
      ;;
    BBB )
    arr=( BB BB BBBB )
      ;;
    CCC )
    arr=()
    ;;
    esac
    echo "${arr[@]}"
}

fillArrayMap(){
  param=
  values=( $(getValues $param) )
  printf "\nIn $param\n"
  echo -e "\nArray values is: ${values[@]}\n"
  printf "\nLength of the array values is : ${#values[@]}\n"
  arrayMap["$param"]=$values #THIS IS THE KEY LINE
  valuesList=${arrayMap[$param]} 
  echo -e "\nArray valuesList is: ${valuesList[@]}\n"
  printf "\nLength of the array valuesList is : ${#valuesList[@]}\n"
}

fillArrayMap AAA
fillArrayMap BBB
fillArrayMap CCC

Now from output I can see valuesListis getting only the first element of the valuesarray. But I want valuesListto contain all the elements returned by the method getValues. i.e

现在从输出中我可以看到valuesList只获取values数组的第一个元素。但我想valuesList包含方法返回的所有元素getValues。IE

valuesList= ${arrayMap[$param]}

now valuesListshould contain all the elements, instead now it contains only 1 element. How to fix that ?

现在valuesList应该包含所有元素,而不是现在它只包含 1 个元素。如何解决?

Note: My goal is to access each individual element like AAA or AA, I don't need it as a whole as a string like AA AAA AAAA

注意:我的目标是访问每个单独的元素,如 AAA 或 AA,我不需要像 AA AAA AAAA 这样的字符串作为一个整体

采纳答案by cdarke

Bash does not support multi-dimensional arrays, but I don't think you need one. You can store a string in the form of a list in an array element, which will give you what you ask for.

Bash 不支持多维数组,但我认为您不需要。您可以在数组元素中以列表的形式存储字符串,这将满足您的要求。

# My made-up version of getServices
getServices() {
    nm=""
    last=${nm##*Type}
    retn=(${last}1 ${last}2 ${last}3 ${last}4)
    echo "${retn[@]}"
}


declare -A serviceList
listService(){
    serviceType=""

    # Here I use the key to make an assignment, which adds to the hash
    serviceList["$serviceType"]=$(getServices $serviceType) 
}

listService serviceTypeA
listService serviceTypeB
listService serviceTypeC

for key in ${!serviceList[@]}
do
    echo "\"$key\": ${serviceList[$key]}"
done

Gives:

给出:

"serviceTypeC": C1 C2 C3 C4
"serviceTypeB": B1 B2 B3 B4
"serviceTypeA": A1 A2 A3 A4

EDIT for new question:

编辑新问题:

alter:

改变:

arrayMap["$param"]=$values     # THIS IS THE KEY LINE
valuesList=${arrayMap[$param]} 

to:

到:

arrayMap["$param"]=${values[@]} 
valuesList=( ${arrayMap[$param]}  )  

When you refer to an array variable by just it's name ($values) you only get the first element.

当您仅通过名称 ( $values)引用数组变量时,您只会获得第一个元素

回答by ghoti

As cdarke already mentioned, bash arrays are one-dimensional. Over the years, folks have come up with ways to "fake" multi-dimensional arrays.

正如 cdarke 已经提到的,bash 数组是一维的。多年来,人们已经想出了“伪造”多维数组的方法。

Two methods I've used are to maintain an array of array descriptions, or an array of pointers to other arrays. I'll answer with the former; the latter should be obvious if you want to explore on your own.

我使用的两种方法是维护一个数组描述数组,或一个指向其他数组的指针数组。我会回答前者;如果你想自己探索,后者应该是显而易见的。

Here's a minimal example of array content getting used to populate variables:

这是用于填充变量的数组内容的最小示例:

#!/usr/bin/env bash

declare -A a=(
  [b]='([0]="one" [1]="two")'
  [c]='([0]="three" [1]="four")'
)

declare -p a

for key in ${!a[@]}; do
  declare -a $key="${a[$key]}"
  declare -p $key
done

Produces:

产生:

declare -A a=([b]="([0]=\"one\" [1]=\"two\")" [c]="([0]=\"three\" [1]=\"four\")" )
declare -a b=([0]="one" [1]="two")
declare -a c=([0]="three" [1]="four")

The critical bit here is that you're using declareto refer to the value of $key, since you can't just say $var="value"in bash.

这里的关键是你declare用来指代 的值$key,因为你不能只$var="value"用 bash说。

Of course, you don't need to name your variables for the value of $keyif you don't want to. Storing values in, say $value, would free you up to use special characters in $key.

当然,如果您不想,您无需为变量命名$key为 。例如$value,在 中存储值可以让您在$key.

An even simpler alternative, if it doesn't offend your sensibilities or restrict your key names too much, is to store the entire output of a declare -pcommand in the value of the array, and then evalit when you need it. For example:

一个更简单的替代方法,如果它不会冒犯您的敏感性或过多地限制您的键名,则是将declare -p命令的整个输出存储在数组的值中,然后eval在您需要时存储。例如:

declare -A a=(
 [b]='declare -a b=([0]="one" [1]="two")'
 [c]='declare -a c=([0]="three" [1]="four")'
)

for key in ${!a[@]}; do
  eval "${a[$key]}"
done

Some people don't like eval.:-) It remains, however in your toolbox.

有些人不喜欢eval:-) 它仍然存在,但是在您的工具箱中。

In your case, it's a little hard to advise because you haven't provided a full MCVE, but here's my contrived example.

就您而言,建议有点困难,因为您尚未提供完整的MCVE,但这是我人为的示例。

#!/usr/bin/env bash

# contrived getServices function, since you didn't provide one
getServices() {
    local -a value=()
    local last="${1:$((${#1}-1)):1}"   # last character of 
    for n in $( seq 1 $(( $RANDOM / 8192 + 1 )) ); do
      value+=(${last}${n})
    done
    declare -p value     # output of this function is actual bash code.
}

# populate the array
listService() {
    servicesList[]=$( getServices  )
}

# Initialize this as empty to make `eval` safer
declare -A servicesList=()

# These services seem interesting.
listService serviceA
listService serviceB
listService serviceC

# Note that we're stepping through KEYS here, not values.
for row in "${!servicesList[@]}"; do
    printf '"%s": ' "$row"
    eval "${servicesList[$row]}"   # Someone is bound to complain about this.
    for column in "${!value[@]}"; do
        # Add whatever $row and $column specific code you like here.
        printf '%s ' "${value[$column]}"
    done
    printf "\n"
done

My output:

我的输出:

$ bash 2dimarrayexample
"serviceC": C1
"serviceB": B1 B2 B3 B4
"serviceA": A1 A2

Of course, your output may differ, since getServices produces random output. :)

当然,您的输出可能会有所不同,因为 getServices 会产生随机输出。:)