Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 301
Daryl Van Sittert Avatar asked May 07 '18 15:05

Daryl Van Sittert


People also ask

Can an array be in a dictionary?

A dictionary's item is a value that is unambiguously associated with a unique key and the key is used to retrieve the item. The key can be of any type except a variant or an array (but generally it is a string or still an integer). The item can be of any type: integer, real, string, variant, object and so on.

Is there a dictionary in bash?

Bash dictionaries give you hash maps and associative arrays in Linux shell scripts. We'll show you how to use these powerful and useful data structures in your own Linux shell scripts.

Does bash support array?

Bash supports one-dimensional numerically indexed and associative arrays types. Numerical arrays are referenced using integers, and associative are referenced using strings.


1 Answers

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").

like image 137
Charles Duffy Avatar answered Nov 15 '22 03:11

Charles Duffy