associative array hack for bash

From: Harald (pifpafpuf_at_gmx.de)
Date: 11/02/05


Date: Wed, 02 Nov 2005 21:23:17 GMT

Below I dare to publish code intended to implement associative arrays
or a storage for key/value pairs. It seems to work for my limited
use. I run this with bash2 and bash3. Note that I try to allow any
special charater for the key as well as the value.

Comments welcome:

a) make it POSIX compliant,
b) improve coding (style),
c) be more efficient (no idea if this is slow),
d) break it, e.g. by storing special characters.

Never mind the yruba_ prefix. I use it in a yruba (see
http://pifpafpuf.de/yruba/userguide.xhtml)

  Thanks,
  Harald Kirsch

#
# A set of functions to simulate an associative array. The associative
# array is stored as a string in any variable. Both, key and value are
# encoded not to contain single vertical bars. Instead vertical bars
# are encoded as "|." A key/value pair is encoded like "||key|=value"
# and in addition, the whole string is terminated by "||".
#

#####
# usage: yruba_remove mapname key
# removes from the map given by mapname the entry for key, if any
function yruba_remove() {
  local table=${!1}
  local key=$(yruba_encode "$2")
  local head=${table%||$key|=*}
  if [ "$head" = "$table" ]; then return; fi
  local tail="${table#*||$key|=*||}"
  eval "$1"='"${head}||${tail}"'
}
#####
# usage: yruba_get mapname key [default]
function yruba_get() {
  # usage: yruba_get mapname
  local table=${!1}
  local key=$(yruba_encode "$2")
  local dflt=$3

  local tail="${table#*||$key|=}"
  if [ "$table" = "$tail" ]; then echo "$dflt"; return; fi
  local value=$(yruba_decode "${tail%%||*}")
  echo "$value"
}
#####
# usage: yruba_put mapname key value
# enters the key/value pair into the map with the name mapname
function yruba_put() {
  if [ -z "${!1}" ]; then eval "$1"='"||"'; fi
  local key=$(yruba_encode "$2")
  local value=$(yruba_encode "$3")
  yruba_remove "$1" "$2"
  local table="${!1}"
  eval "$1"='"||$key|=${value}$table"'
}
#####
# usage: mapname aryname
# Enters all keys found in the map with name mapnam into the array
# given by aryname.
# Example: yruba_keys map keys; for x in "${keys[@]}"; do...done;
function yruba_keys() {
  local table=${!1}
  local aname=$2
  local l
  while [ "$table" != "||" ]; do
    local key=$(yruba_decode "${table%%|=*}")
    eval l=\${\#${aname}[@]}
    eval ${aname}[$l]='${key#||}'
    table="||${table#||*||}"
  done
}
function yruba_encode() {
  local p=${1//|/|.}
  echo "$p"
}
function yruba_decode() {
  local p=${1//|./|}
  echo "$p"
}
########################################################################

# Unsorted examples and test cases.
yruba_put ary bsq "\\'"
yruba_put ary quote \'
yruba_put ary dquote '"'
yruba_put ary '"' aaaa
yruba_put ary \' bbb
yruba_put ary "a
a" bbb
yruba_put ary "X X" "y y"
yruba_put ary " ' ' ' " ' " " " '
yruba_put ary ' " " " ' " ' ' ' "
echo $ary

yruba_keys ary keys
for x in "${keys[@]}"; do
  y=$(yruba_get ary "$x")
  echo ">>$x<<" ">>$y<<"
  yruba_remove ary "$x"
done
echo ">>$ary<<"

-- 
---------------------+---------------------------------------------
Harald Kirsch (@home)| 
Java Text Crunching: http://www.ebi.ac.uk/Rebholz-srv/whatizit/software