I'm trying to manage a list of associative arrays with an array in bash, and I can't seem to pick up what's not going right.
What I'm trying to do:
array=(a b c d)
for i in ${array[@]}; do
declare -A $i
done
a[key]=avalue
b[key]=bvalue
c[key]=cvalue
d[key]=dvalue
That all seems to work fine as I can manually return values by referencing ${a[key]}
just fine.
However, when I'm trying to iterate through using the array
variable it's not really giving me what I expect.
for index in ${array[@]}; do
echo ${index[key]}
done
Is returning the same as if I were to run
for index in ${array[@]}; do
echo $index
done
I feel like I'm missing something simple, but searching for an answer isn't turning up any solutions. Any assistance would be appreciated.
Here's one solution, using shell indirection. This will work with any bash
which supports associative arrays. The indirection must contain the entire reference, including subscripts, which is a bit awkward.
for index in "${array[@]}"; do
indexkey=${index}[key]
echo "${!indexkey}"
done
With a modern bash
(at least v4.3), you can use nameref declarations, which create aliases. This is a lot more convenient, because you can use different keys with the same alias:
for index in "${array[@]}"; do
declare -n subarray=$index
echo "${subarray[key]}"
done
As Etan Reisner points out in a comment, this question is dealt with at some length in a Bash FAQ entry. However, on the date that I write, that FAQ entry includes the disclaimer "Overhauling this page will take some time and work", and it is certainly true that the FAQ entry is not currently as clear as one might like. So here's my brief summary:
declare
(and other related builtins, including export
, local
, and typeset
) evaluate their arguments. So you can build up a variable name in a declare
statement.
Since declare
can also be used to assign values (as long as you use the same declaration), you can build up an index variable name in a declare statement.
If you are using bash4.3 or better, you can use namerefs (typeset -n
or declare -n
) in order to simulate array values. That's really the only way you can return an array from a bash function, but it is still a bit awkward since it requires the caller of the function to provide an argument with the name of the array; furthermore, it is not entirely robust since the name will be used in the scope of the function so it might be shadowed by a local variable. Caution is required.
There is probably no other good reason to use variable indirection. If you find yourself needing it, think about whether you could structure your program differently. You can collapse associative keys with string concatenation. For example:
my_array[${place}:${feature}]
will work find as long as no value for ${place}
contains a colon (of course, you could use a different character instead of colon).
Watch out with keys. If an array is declared associative, the key is evaluated more or less normally, but if it is a normal indexed array, the key is evaluated as an arithmetic expression. The consequence is that
declare -A assoc
declare -a indexed
key=42
# This assigns the element whose key is "key"
assoc[key]=foo
# This assigns the element whose key is 42
indexed[key]=foo
# If $key had not been defined, the above would have assigned
# the element whose key was 0, without an error message
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With