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

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

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

basically my code is as follows

 listService(){ serviceType=$1 servicesList=($(getServices $serviceType)) } listService serviceTypeA listService serviceTypeB listService serviceTypeC 

here getServices is a function that returns an array of services based on the argument passed as $serviceType . Therefore, every time I call the listService function, my serviceList becomes an overridden new list of services. But I want to save all services from different types of services in the form of such a card:

 "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 @cdarke's answer. Here is my code now:

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

Now from the output, I see that the valuesList gets only the first element of the values array. But I want the valuesList contain all the elements returned by the getValues method. iee

 valuesList= ${arrayMap[$param]} 

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

Note. My goal is to access each individual element, for example, AAA or AA, I do not need it as a whole as a string, for example AA AAA AAAA

+5
source share
2 answers

Bash does not support multidimensional arrays, but I don't think you need it. You can save the string as a list in an array element that will give you what you are asking for.

 # 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 

gives:

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

EDIT for a new question:

edit:

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

in

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

When you reference an array variable with only the name ( $values ), you get the first element .

+3
source

As mentioned earlier, bash arrays are one-dimensional. Over the years, people have come up with ways to β€œfake” multidimensional arrays.

The two methods I used were to maintain an array of array descriptions or an array of pointers to other arrays. I will answer first; the latter should be obvious if you want to explore on your own.

Here is a minimal example of the contents of an array that is used to populate variables:

 #!/usr/bin/env bash declare -A a=( [b]='([0]="one" [1]="two")' [c]='([0]="three" [1]="four")' ) declare -pa for key in ${!a[@]}; do declare -a $key="${a[$key]}" declare -p $key done 

It produces:

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

The critical bit here is that you use declare to indicate the value of $key , since you cannot just say $var="value" in bash.

Of course, you do not need to specify variables for the value of $key unless you want to. Storing values ​​in, for example $value , will free you from using special characters in $key .

An even simpler alternative, if it does not offend your sensitivity or limits your key names too much, is to store the entire output of the declare -p command in an array value and then eval when you need it. For instance:

 declare -A a=( [b]='declare -ab=([0]="one" [1]="two")' [c]='declare -ac=([0]="three" [1]="four")' ) for key in ${!a[@]}; do eval "${a[$key]}" done 

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

In your case, a little advice, because you did not provide the full MCVE , but here is my contrived example.

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

My conclusion:

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

Of course, your output may be different, as getServices produces random output. :)

+2
source

Source: https://habr.com/ru/post/1262875/


All Articles