6

I've got a variable list of associative arrays that I want to iterate through and retrieve their key/value pairs.

I iterate through a single associative array by listing all its keys and getting the values, ie.

for key in "${!queue1[@]}" do
    echo "key : $key"
    echo "value : ${queue1[$key]}"
done

The tricky part is that the names of the associative arrays are variable variables, e.g. given count = 5, the associative arrays would be named queue1, queue2, queue3, queue4, queue5.

I'm trying to replace the sequence above based on a count, but so far every combination of parentheses and eval has not yielded much more then bad substitution errors. e.g below:

for count in {1,2,3,4,5} do
    for key in "${!queue${count}[@]}" do
        echo "key : $key"
        echo "value : ${queue${count}[$key]}"
    done
done

Help would be very much appreciated!

1
  • The existing answers are largely out-of-date in the context of namevar support in bash 4.3 (and later). Commented Sep 29, 2016 at 20:52

3 Answers 3

3

The difficulty here stems from the fact that the syntax for indirect expansion (${!nameref}) clashes with the syntax for extracting keys from an associative arrays (${!array[@]}). We can have only one or the other, not both.

Wary as I am about using eval, I cannot see a way around using it to extract the keys of an indirectly referenced associative array:

keyref="queue${count}[@]"
for key in $(eval echo '${!'$keyref'}'); do ... ; done

You can however avoid eval and use indirect expansion when extracting a value from an array given the key. Do note that the [key] suffix must be part of the expansion:

valref="queue${count}[$key]"
echo ${!valref}

To put this in context:

for count in {1..5} ; do
    keyref="queue${count}[@]"
    for key in $(eval echo '${!'$keyref'}'); do
        valref="queue${count}[$key]"
        echo "key = $key"
        echo "value = ${!valref}"
    done
done
Sign up to request clarification or add additional context in comments.

Comments

1

I was able to make it work with the following script:

for count in {1..5} ; do
    for key in $(eval echo '${!q'$count'[@]}') ; do
        eval echo '${q'$count"[$key]}"
    done
done

Note it breaks if any key contained a space. If you want to deal with complex data structures, use a more powerful language like Perl.

Comments

0

I think this might work (but untested). The key is to treat the indexing as the full name of a variable. (That is, the array queue5 can be treated as a sequence of variables named queue5[this], queue5[that], etc.)

for count in {1,2,3,4,5} do
    assoc="queue$count[@]"
    for key in "${!assoc}" do
        echo "key : $key"
        val="queue$count[$key]"
        echo "value : ${!val}"
    done
done

2 Comments

This seems to iterate through the values in the array (instead of the keys in the array). I guess you can dig out the key using the value with some work, but there might be a problem with non-unique values.
Indirect referencing works for accessing array values but not for keys. See my answer for details.

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.