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).
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 )
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.
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!
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).
No comments yet.
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