Tim Bezhashvyly
Tim Bezhashvyly

Reputation: 9100

Get keys of an array with variable name in bash

In my bash script I have two arrays. Depending on some logic either one or another shall be used, so I'm getting the name of a required array in a variable varName. I can surely get the values of this array with the code below but is there a way to get it's keys? Tried several options but no luck.

declare -A foo=([a]=b [c]=d)
declare -A bar=([e]=f [g]=h)

varName=foo
varArray=$varName[@]
echo ${!varArray}

Thanks.

Upvotes: 3

Views: 488

Answers (2)

bandie
bandie

Reputation: 183

There is an IFS-safe method to get the keys (or values) of an array indirectly:

declare -a things=("an apple" "a banana")
declare -a tmp
arrayName=things
eval "tmp=(\"\${$arrayName[@]}\")"
# "${tmp[@]}" is now ("an apple" "a banana")

Upvotes: 0

chepner
chepner

Reputation: 531275

Not without resorting to eval, unfortunately. To be safe, make sure varName is just a single valid identifier.

[[ varName =~ ^[a-zA-Z_][a-zA-Z_0-9]+$ ]] && eval "echo \${!$varName[@]}"

eval is necessary to provide a second round of parsing and evaluation. In the first round, the shell performs the usual parameter expansion, resulting in the string echo ${!foo[@]} being passed as the single argument to eval. (In particular, the first dollar sign was escaped and so is passed literally; $varName is expanded to foo; and the quotes are removed as part of quote removal. eval then parses that string and evaluates it.

$ eval "echo \${!$varName[@]}"
#       echo  ${!foo     [@]}
#  The above is the argument that `eval` sees, after the shell
#  does the normal evaluation before calling `eval`. Parameter
#  expansion replaces $varName with foo and quote removal gets
#  rid of the backslash before `$` and the double quotes.
a c

If you are using bash 4.3 or later, you can use a nameref.

declare -n varName=foo
for key in "${!varName[@]}"; do
    echo "$key"
done

Upvotes: 3

Related Questions