How to create associative array from a comma separated string with key/value pairs in Bash script

Written by - 0 comments

Published on - Listed in Linux Shell Bash Coding


Was looking for a way to read an input string which contains multiple keys and values and then compare the values with another lookup, based on the key (name).

Which input format?

At first I thought about declaring the input string as a classic array, which by default ($IFS) is space separated:

ck@mint ~ $ declare -a input=( "key1=value1" "key2=value2" "key3=value3" )

This list works fine when manually declared inside the script. However my goal was to have a user entering the input string with a getopts/parameter. Having spaces between double-quoted key/value pairs often leads to users doing mistakes. Yep, I'm talking from experience  ^_^.

A better way to enter a list of key value pairs is to comma-separate them. I've seen in other scripts (such as monitoring-plugins) that users make less typos and is better readable (for me personally at least).

So I decided to move to a comma-separated input:

ck@mint ~ $ declare -a input=( key1=value1,key2=value2,key3=value3 )

The IFS separator

In Bash you can use a while read or a for loop to go through an array and do additional parsing. However, as mentioned above, the Bash-internal IFS variable is by default set to " " (a space) to separate array entries.

For my comma-separated array $input I need to temporarily set IFS to a comma, followed by a very simple loop through the whole array:

ck@mint ~ $ declare -a input=( key1=value1,key2=value2,key3=value3 )
ck@mint ~ $ OIFS="$IFS"
ck@mint ~ $ IFS=","
ck@mint ~ $ for pair in ${input[*]}; do echo $pair; done
key1=value1
key2=value2
key3=value3
ck@mint ~ $ IFS="$OIFS"

Note: I've reset IFS back to the original value right after handling the comma-separated array.

Create associative array from key value pairs

The next step was to create an associative array from these key/value pairs.

Usually arrays would have a "variable  name" with an index (number) to access single values within an array ($array[0]), but an associative array is perfect for handling key/value pairs because the "key can be used as array index" (hope this explanation makes sense).

Screenshot from linked site above

The array itself is called expected_value and each key is used to add a new entry to the array, identifiable with the key:

ck@mint ~ $ declare -a input=( key1=value1,key2=value2,key3=value3 )
ck@mint ~ $ declare -A expected_value
ck@mint ~ $ OIFS="$IFS"
ck@mint ~ $ IFS=","
ck@mint ~ $ for pair in ${input[*]}; do
> k=$(echo $pair  | awk -F'=' '{print $1}')
> v=$(echo $pair  | awk -F'=' '{print $2}')
> echo "Key is $k and value is $v"
> expected_value[${k}]=${v}
> done
Key is key1 and value is value1
Key is key2 and value is value2
Key is key3 and value is value3
ck@mint ~ $ IFS="$OIFS"

Now that each key/value pair is stored in its own associative array, we can retrieve the value of a wanted entry. Let's use "key2" for example:

ck@mint ~ $ echo ${expected_value[key2]}
value2

Sweet!

Using associative array in another loop for value comparison

Now I can use the array in another for loop and compare the live values (e.g. from an API) to the expected values. Let's use the Salesforce Status API, which I've used in a recent article showing how to extract the keys from a JSON output:

ck@mint ~ $ apicall=$(curl -s "https://api.status.salesforce.com/v1/instances/CS110/status")
ck@mint ~ $ declare -a api_keys=( $(echo "${apicall}" | jq -r '.Services[].key' | tr "\n" " ") )
ck@mint ~ $ declare -a api_values=( $(echo "${apicall}" | jq -r '.Services[].isCore' | tr "\n" " ") )

ck@mint ~ $ declare -a input=( coreService=true,liveAgent=true,EinsteinBots=false,Communities=false,search=true )
ck@mint ~ $ declare -A expected
ck@mint ~ $ OIFS="$IFS"
ck@mint ~ $ IFS=","
ck@mint ~ $ for pair in ${input[*]}; do
> k=$(echo $pair  | awk -F'=' '{print $1}')
> v=$(echo $pair  | awk -F'=' '{print $2}')
> echo "Key is $k and value is $v"
> expected[${k}]=${v}
> done
Key is coreService and value is true
Key is liveAgent and value is true
Key is EinsteinBots and value is false
Key is Communities and value is false
Key is search and value is true
ck@mint ~ $ IFS="$OIFS"

ck@mint ~ $ n=0
ck@mint ~ $ for service in ${api_keys[*]}; do
> echo "Handling ${service}"
> currentstatus=$(echo ${api_values[${n}]})
> expectedstatus=${expected[${service}]}
> if [[ -n ${expectedstatus} && ${currentstatus} != ${expectedstatus} ]]; then
> echo "Oh no! ${service} is ${currentstatus} but should be ${expectedstatus}"
> fi
> let n++
> done
Handling coreService
Oh no! coreService is false but should be true
Handling liveAgent
Oh no! liveAgent is false but should be true
Handling EinsteinBots
Handling Communities
Handling search
Oh no! search is false but should be true
Handling Customer360Audiences
Handling analytics
Handling CPQandBilling
Handling ServiceCloudVoice
Handling SalesforceCMS
Handling SALESFORCEORDERMANAGEMENT
Handling B2BCommerce
Handling B2B2CCommerce
Handling SalesforceUnifiedMessaging
Handling ServiceCloudMessagingChannels

And this is basically how I can now do a value comparison from expected values (from the user input) to live values (from the API).


Add a comment

Show form to leave a comment

Comments (newest first)

No comments yet.

RSS feed

Blog Tags:

  AWS   Android   Ansible   Apache   Apple   Atlassian   BSD   Backup   Bash   Bluecoat   CMS   Chef   Cloud   Coding   Consul   Containers   CouchDB   DB   DNS   Database   Databases   Docker   ELK   Elasticsearch   Filebeat   FreeBSD   Galera   Git   GlusterFS   Grafana   Graphics   HAProxy   HTML   Hacks   Hardware   Icinga   Influx   Internet   Java   KVM   Kibana   Kodi   Kubernetes   LVM   LXC   Linux   Logstash   Mac   Macintosh   Mail   MariaDB   Minio   MongoDB   Monitoring   Multimedia   MySQL   NFS   Nagios   Network   Nginx   OSSEC   OTRS   Office   PGSQL   PHP   Perl   Personal   PostgreSQL   Postgres   PowerDNS   Proxmox   Proxy   Python   Rancher   Rant   Redis   Roundcube   SSL   Samba   Seafile   Security   Shell   SmartOS   Solaris   Surveillance   Systemd   TLS   Tomcat   Ubuntu   Unix   VMWare   VMware   Varnish   Virtualization   Windows   Wireless   Wordpress   Wyse   ZFS   Zoneminder