Daryl Van Sittert
Daryl Van Sittert

Reputation: 877

Using Bash, is it possible to store an array in a dictionary

With bash, it is possible to store an array in a dictionary? I have shown some sample code of fetching an array from the dictionary but it seems to lose the fact that it is an array.

I expect it is the dict+=(["pos"]="${array[@]}") command but am unsure of how to do this or if it is even possible.

# Normal array behaviour (just an example)
array=(1 2 3)
for a in "${array[@]}"
do
    echo "$a"
done
# Outputs:
# 1
# 2
# 3

# Array in a dictionary
declare -A dict
dict+=(["pos"]="${array[@]}")

# When I fetch the array, it is not an array anymore
posarray=("${dict[pos]}")
for a in "${posarray[@]}"
do
    echo "$a"
done
# Outputs:
# 1 2 3
# but I want
# 1
# 2
# 3

Upvotes: 6

Views: 4308

Answers (2)

Charles Duffy
Charles Duffy

Reputation: 295472

No, but there are workarounds.


Using printf '%q ' + eval

You can flatten your array into a string:

printf -v array_str '%q ' "${array[@]}"
dict["pos"]=$array_str

...and then use eval to expand that array back:

# WARNING: Only safe if array was populated with eval-safe strings, as from printf %q
key=pos; dest=array
printf -v array_cmd "%q=( %s )" "$dest" "${dict[$key]}"
eval "$array_cmd"

Note that this is only safe if your associative array is populated through the code using printf '%q ' to escape the values before they're added; content that avoids this process is potentially unsafe to eval.


Using base64 encoding

Slower but safer (if you can't prevent modification of your dictionary's contents by untrusted code), another approach is to store a base64-encoded NUL-delimited list:

dict["pos"]=$(printf '%s\0' "${array[@]}" | openssl enc base64)

...and read it out the same way:

array=( )
while IFS= read -r -d '' item; do
  array+=( "$item" )
done < <(openssl enc -d base64 <<<"${dict["pos"]}"

Using Multiple Variables + Indirect Expansion

This one's actually symmetric, though it requires bash 4.3 or newer. That said, it restricts your key names to those which are permissible as shell variable names.

key=pos
array=( "first value" "second value" )

printf -v var_name 'dict_%q' "$key"
declare -n var="$var_name"
var=( "${array[@]}" )
unset -n var

...whereafter declare -p dict_pos will emit declare -a dict_pos=([0]="first value" [1]="second value"). On the other end, for retrieval:

key=pos
printf -v var_name 'dict_%q' "$key"
declare -n var="$var_name"
array=( "${var[@]}" )
unset -n var

...whereafter declare -p array will emit declare -a array=([0]="first value" [1]="second value").

Upvotes: 4

John Kugelman
John Kugelman

Reputation: 361685

Dictionaries are associative arrays, so the question rephrased is: "Is it possible to store an array inside another array?"

No, it's not. Arrays cannot be nested.

dict+=(["pos"]="${array[@]}")

For this to work you'd need an extra set of parentheses to capture the value as an array and not a string:

dict+=(["pos"]=("${array[@]}"))

But that's not legal syntax.

Upvotes: 2

Related Questions