2017-01-16 40 views
4

我想在shell中创建映射。每个值都是一个数组。所以地图是关键:数组对。例如,它可以是这样的:如何在shell中创建key:array的映射?

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

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

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

listService serviceTypeA 
listService serviceTypeB 
listService serviceTypeC 

这里getServices是返回基于为$serviceType传入的参数一系列的服务功能。所以每次我拨打listService函数时,我的serviceList都会被新的服务列表覆盖。但我希望将所有的服务来自不同服务类型的地图像这样的形式:

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

之后,我想访问基于密钥的每个阵列。如何实现这一点。

在此先感谢您的帮助。

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

#!/bin/bash 
declare -A arrayMap 

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

fillArrayMap(){ 
    param=$1 
    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 

现在从输出我可以看到valuesList越来越仅values数组的第一个元素。但我想要valuesList包含方法getValues返回的所有元素。即

valuesList= ${arrayMap[$param]} 

现在valuesList应包含所有要素,而不是现在只包含一个元素。如何解决这个问题?

注:我的目标是访问每个元素像AAA或AA,我并不需要它作为一个整体像AA AAA AAAA字符串

+0

gawk有2-d数组,(实际上它是由hastable实现的),你可以考虑使用awk,如果你的其他逻辑可以被拖入。 – Kent

+1

或在python中完成你的工作。 ;-) – Kent

回答

3

击不支持多维数组,但我不要以为你需要一个。你可以在数组元素中以列表的形式存储一个字符串,这会给你你所要求的。

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


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

    # 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 

得出:对于新问题

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

编辑:

改变:

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

到:

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

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

+0

感谢您的回答。我试过并遇到另一个问题。问题详情中添加了相同的内容。你可以请建议任何@cdarke? – saurav

2

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

的两种方法是保持阵列描述,或指针数组以其他阵列的阵列。我会回答前者;后者应该是明显的,如果你想自己探索。

这里的习惯来填充变量数组内容的小例子:

#!/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 

产地:

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") 

这里的关键一点是,你使用declare指的价值$key,因为你不能在bash中说$var="value"

当然,如果您不想要,您不需要为$key的值命名变量。将数值存储在$value中会使您免费使用$key中的特殊字符。

一个更简单的替代方案,如果它不会冒犯你的敏感性或限制你的密钥名太多,就是将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. :-)它仍然存在,但在你的工具箱。

就你而言,有点难以建议,因为你没有提供完整的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 $1 
    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[$1]=$(getServices $1) 
} 

# 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 

我的输出:

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

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

+0

为什么输出的顺序与您运行listService的顺序不同?另外,以这种方式使用'eval'有什么风险?一定很微妙,我根本看不到它。 – Graham

+1

@Graham - 关联数组不在bash中保持顺序。如果您需要保持秩序,您可以维护一个单独的非关联(编号)数组,指向关联数组中的关键字,然后使用'for'循环遍历第二个数组以保证您的订单。 – ghoti