BashHashes

Greetings

Bash 4.x has associative arrays (hashes if you are an old perl user like me). The existing examples of how to use them on the internet are not very well documented, so I thought I would write up my experience from a recent real world project.

Note this only works for bash 4.x.

First Example

Use a while/read loop to process a file line by line. Here is a sample input file (file.txt) which comes from the aws cli 'describe-snapshots' output (in text mode). The fields are tab-delimited.

SNAPSHOTS       service backup 2016-09-06   False           134583324031    100%    snap-13456582   2016-09-06T18:19:13.000Z        completed       vol-b13fc255    1024
SNAPSHOTS       service backup 2016-09-07   False           134583324031    100%    snap-54938492   2016-09-07T00:00:09.000Z        completed       vol-b13fc255    1024
SNAPSHOTS       service backup 2016-09-08   False           134583324031    100%    snap-32949585   2016-09-08T00:00:09.000Z        completed       vol-b13fc255    1024

we need to retrieve the snapshot ids and the date each was created, so that we can determine if a snapshot is old enough to be deleted.

# assoc array to hold the results
declare -A snapIds

# read the file and extract id and date for each line
while read line; do
  snapId=$(echo "${line}" | cut -f6)
  # we don't care about the specific time, just the date
  date=$(echo "${line}" | cut -f7 | cut -dT -f1)
  # create hash entry with key of snapId and value of date
  snapIds["${snapId}"]="${date}"
done < file.txt

Second Example

Same as example one, but process the contents of a multiline variable, instead of reading a file, by use of a bash here-string:

# assoc array to hold the results
declare -A snapIds

# Get all the matching snapshots from aws
snapshotDump=$(aws ec2 describe-snapshots --region us-west-2 \
                                --filters "Name=description,Values=service backup*" \
                                --output text | grep "^SNAPSHOTS")

# read the file and extract id and date for each line
while read line; do
  snapId=$(echo "${line}" | cut -f6)
  # we don't care about the specific time, just the date
  date=$(echo "${line}" | cut -f7 | cut -dT -f1)
  # create hash entry with key of snapId and value of date
  snapIds["${snapId}"]="${date}"
done <<< ${snapshotDump}

Manipulating Hashes

In both cases the result is the same, a hash of each snapshot id and the date it was created. We can view the results like so:

for id in "${!snapIds[@]}"; do
  echo "id: ${id} date: ${snapIds[${id}]}"
done

Some things to note here:

  • you always have to reference hashes inside ${}
  • ${!snapIds} (with the !) gets you the keys. Without the !, you get the values.
  • similar to bash arrays, ${!snapIds[@]} give you all the keys. Thus, ${snapIds[@]} gets all the values.

Now you could use some code like this to run through the hash keys and do something with each value:

# determine epoch date for one year ago:
oneYearAgo=$(date -d "1 year ago" +%s)

for snapId in "${!snapIds[@]}"; do
  date=${snapIds["${snapId}"]}
  # convert date to epoch time
  epochDate=$(date --date="${date}" +%s)
  if [ $epochDate -lt $oneYearAgo ]; then
    # snapshot is more than one year old, delete it.
    aws ec2 delete-snapshot --snapshot-id "${snapId}" --region us-west-2
  fi
done

Conclusion

I hope you've enjoyed these examples of how to use some more advanced bash 4.x features. I know it may seem silly to do all this work in bash, but the facilities are there if you want to use them.


CategoryGeekStuff
CategoryLinuxStuff



Our Founder
ToolboxClick to hide/show