Search code examples
arraysbashloopsmultidimensional-array

Bash: How to get value from named key in multi-dimensional array


I get a "bad substitution" error when trying to get a named key from a dynamically-referenced array.

GNU Bash version 4.1.2(2)

WORKS:

declare -rA DBONE=([system_name]='ABC' [dbname]='abc_database' [dbusername]='user1')
declare -rA DBTWO=([system_name]='DEF' [dbname]='def_database' [dbusername]='user2')
declare -rA DBTHREE=([system_name]='XYZ' [dbname]='xyz_database' [dbusername]='user3')

# works: get value when using the real name of the array, e.g. "DBTWO"
echo ${DBTWO["dbname"]}

Output: "def_database" (correct)

ERROR: bad substitution

declare -rA DBONE=([system_name]='ABC' [dbname]='abc_database' [dbusername]='user1')
declare -rA DBTWO=([system_name]='DEF' [dbname]='def_database' [dbusername]='user2')
declare -rA DBTHREE=([system_name]='XYZ' [dbname]='xyz_database' [dbusername]='user3')

# create an array of the arrays
declare -a databases=(DBONE DBTWO DBTHREE)

# get the first array (e.g. DBONE)
database="${databases[0]}"
echo "The dbname is ${$database[dbname]}"
echo "The dbname is ${$database}[dbname]"

Output:

./myscript.sh: ${$database[dbname]}: bad substitution
./myscript.sh: ${$database}[dbname]: bad substitution

Ultimately, my final goal is to loop over the array of arrays, like this:

# create an array of the arrays
declare -a databases=(DBONE DBTWO DBTHREE)
for ((i=0; i<"${#databases[*]}"; i++)); do
    database="${databases[$i]}"
    dbname="${database[dbname]}"
    echo "The database is $database, and the dbname is $dbname"
done    

Note: my version of bash (GNU Bash version 4.1.2(2)) does not support declare "-n". It allows these options: declare: usage: declare [-aAfFilrtux] [-p] [name[=value] ...]


Solution

  • You can use indirect reference :

    declare -rA DBONE=([system_name]='ABC' [dbname]='abc_database' [dbusername]='user1')
    declare -rA DBTWO=([system_name]='DEF' [dbname]='def_database' [dbusername]='user2')
    declare -rA DBTHREE=([system_name]='XYZ' [dbname]='xyz_database' [dbusername]='user3')
    
    declare -a databases=(DBONE DBTWO DBTHREE)
    for ((i=0; i<"${#databases[*]}"; i++)); do
        database="${databases[$i]}"
        ref="$database[dbname]"
        dbname="${!ref}"
        echo "The database is $database, and the dbname is $dbname"
    done