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. :)