3

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.

0

2 Answers 2

3

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
Sign up to request clarification or add additional context in comments.

2 Comments

So the result of the evaluation will be a string, right? That means it can't be used for for loop, right? The option would be to then pass it through IFS or is there more elegant solution? Once again thank you.
Yes, it would be a single string, so keys that themselves contain whitespace will present a problem. I'm not sure I can think of a better solution in bash 4.2 or earlier. I just remembered that you can use a nameref in bash 4.3, if that is an option.
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.