Here is a bash3 associative array hack which satisfies:
- pure bash (no fork/exec)
- no subshell
- constant time fetch (in terms of number of keys)
- any metachars allowed in keys
The basic idea is to encode keys by substituting non-identifier chars with their hex value, and then use the sanitized key (with a name prefix) as a bash local var, leveraging the constant time bash name lookup.
enc-key()
{
local key="${1}" varname="${2:-_rval}" prefix="${3:-_ENCKEY_}"
local i converted
local -a enc_parts convert
local re='^([[:alnum:]]*)([^[:alnum:]]+)(.*)'
local nonalnum
enc_parts+=( "${prefix}" )
while [[ $key =~ $re ]]; do
enc_parts+=( "${BASH_REMATCH[1]}" )
nonalnum="${BASH_REMATCH[2]}"
key="${BASH_REMATCH[3]}"
convert=()
for (( i = 0; i < ${#nonalnum}; i++ )); do
# leading ' in string signals printf to covert char to ascii
convert+=( "'${nonalnum:$i:1}" )
done
printf -v converted "_%x" "${convert[@]}"
enc_parts+=( "${converted}" )
done
enc_parts+=( "${key}" )
printf -v $varname "%s" "${enc_parts[@]}"
echo "DEBUG: final key: ${!varname}"
return 0
}
To store:
local key
enc-key 'my-fine-key!' key
local "${key}=value"
Fetch (before exiting function scope of the store):
enc-key 'some other key' key
fetched="${!key}"
Note that the second arg of enc-key is the name of the var into which enc-key will store the sanitized key.
The question: is there a way to do the encoding that does not involve character based traversal with many re matches along the way? Either some printf magic or var sub voodoo?