Bash – Indirect Parameter Expansion in Associative Array

associative arraybashparameter

Following this answer, I want to apply the approach on my script. The basics of it is:

foo="bar" 
baz="foo"
echo "${!baz}"
bar

I want to provide translated strings, like this:

declare -A es
declare -A en

#
es["greet"]="Hola"
en["greet"]="Hello"
#
es["world"]="Mundo"
en["world"]="World"

Then at the top of my script I would set:

lang="es"

then

echo "${!lang["greet"]} ${!lang["world"]}"

I would expect the output:

Hola Mundo

or

lang="en"

echo "${!lang["greet"]}" "${!lang["world"]}"

Hello World

But all I get are empty strings.

Is it possible to do this or should I explore other ways?


So far I've come with two approaches, none of them I like:

declare -A hello
hello["es"]="Hola"
hello["en"]="Hello"

declare -A world
world["es"]="Mundo"
world["en"]="World"

lang="es"

echo "${hello[$lang]} ${world[$lang]}"
Hola Mundo

The other:

lang="es"

if [[ "$lang" = "es" ]]; then
  declare -A language=(
    ["greet"]="Hola"
    ["world"]="Mundo"
  )
elif [[ "$lang" = "en" ]]; then
  declare -A language=(
    ["greet"]="Hello"
    ["world"]="World"
  )
fi

echo "${language["greet"]} ${language["world"]}"
Hola Mundo

Best Answer

Use a name-reference variable instead of indirection.

With your initial set-up of the associative arrays en and es:

$ declare -n lang=en
$ echo "${lang["greet"]} ${lang["world"]}"
Hello World
$ declare -n lang=es
$ echo "${lang["greet"]} ${lang["world"]}"
Hola Mundo

The value of the lang name-reference is the name of another variable. Accessing the variable lang will be the same as accessing that named variable.

Name-reference variables are available in the bash shell since release 4.3.

Related Question