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

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


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.

# My made-up version of getServicesgetServices() {    nm="$1"    last=${nm##*Type}    retn=(${last}1 ${last}2 ${last}3 ${last}4)    echo "${retn[@]}"}declare -A serviceListlistService(){    serviceType="$1"    # Here I use the key to make an assignment, which adds to the hash    serviceList["$serviceType"]=$(getServices $serviceType) }listService serviceTypeAlistService serviceTypeBlistService serviceTypeCfor 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 LINEvaluesList=${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.


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

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 bashdeclare -A a=(  [b]='([0]="one" [1]="two")'  [c]='([0]="three" [1]="four")')declare -p afor key in ${!a[@]}; do  declare -a $key="${a[$key]}"  declare -p $keydone

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 declare to refer to the value of $key, since you can't just say $var="value" in bash.

Of course, you don't need to name your variables for the value of $key if you don't want to. Storing values in, say $value, would free you up to use special characters in $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 -p command in the value of the array, and then eval it when you need it. For example:

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.

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

#!/usr/bin/env bash# contrived getServices function, since you didn't provide onegetServices() {    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 arraylistService() {    servicesList[$1]=$( getServices $1 )}# Initialize this as empty to make `eval` saferdeclare -A servicesList=()# These services seem interesting.listService serviceAlistService serviceBlistService 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. :)