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 :
"Key1" : a1 a2 a3 a4
"key2" : b1 b2 b3
"key3" : c1
basically my code looks like this
listService(){
serviceType=$1
servicesList=($(getServices $serviceType))
}
listService serviceTypeA
listService serviceTypeB
listService serviceTypeC
here getServices
is a function which returns an array of services based on the argument passed as $serviceType
. So every time i call the listService
function my serviceList
gets overridden by new service list. But I want to keep all the services from different service type in form of a map like this :
"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 :
#!/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 output I can see valuesList
is getting only the first element of the values
array. But I want valuesList
to contain all the elements returned by the method getValues
. i.e
valuesList= ${arrayMap[$param]}
now valuesList
should contain all the elements, instead now it contains only 1 element. How to fix that ?
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
There are two types of arrays in Bash: indexed arrays – where the values are accessible through an integer index. associative arrays – where the values are accessible through a key (this is also known as a map)
$_ (dollar underscore) is another special bash parameter and used to reference the absolute file name of the shell or bash script which is being executed as specified in the argument list. This bash parameter is also used to hold the name of mail file while checking emails. $@
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 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 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.
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 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 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 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 output:
$ bash 2dimarrayexample
"serviceC": C1
"serviceB": B1 B2 B3 B4
"serviceA": A1 A2
Of course, your output may differ, since getServices produces random output. :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With