Friday, October 16, 2009

associative arrays in bash

Bash 4 will have built-in associative arrays. But until then here is a nice trick I found (the least invasive that I found) to emulate a similar behavior.
function idx {
cnt=${#cases[*]};
eval 'case $1 in '${cases[*]}' *) [ "$1" ] && { cases['$cnt']='\''"'\''$1'\''") echo '$cnt';;'\''; echo '$cnt';}; esac';
}

Then you can do
arr[`idx john`]=doe
echo ${arr[`idx john`]}

Credit for this trick goes to Tafuni Vito

The idea is this: every time the function idx is called, it appends a strings to the cases array - composed of the passed variable, made ready for bash's case statements (in example above it would be "john"), then the word "echo" (without quotes), and finally the string "N;;" where N is the current size of the cases array. So if the call "idx john" was the first call, the the first element of the cases array would be
"john") echo 0;;
if we then call "idx jack", the cases array would look like this:
"john") echo 0;;
"jack") echo 1;;
This means that the expression "case $1 in ${cases[*]}" will look like this:
case $1 "john") echo 0;; "jack") echo 1;;
Which is exactly the appropriate beginning for a bash switch statement. The rest of the function handles the "default" case of the switch, by appending to the cases array, and returning its current size.