1

I'm writing a bash script to analyse some files. In a first iteration I create associative arrays with word counts for each category that is analysed. These categories are not known in advance so the names of these associative arrays are variable but all with the same prefix count_$category. The associative arrays have a word as key and its occurrence count in that category as value.

After all files are analysed, I have to summarise the results for each category. I can iterate over the variable names using ${count_*} but how can I access the associative arrays behind those variable names? For each associative array (each count_* variable) I should iterate over the words and their counts.

I have already tried with indirect access like this but it doesn't work:

for categorycount in ${count_*} # categorycount now holds the name of the associative array variable for each category
do
    array=${!categorycount}
    for word in ${!array[@]}
    do
        echo "$word occurred ${array[$word]} times"
    done
done
6
  • 1
    Can you post a minimal reproducible example ? For instance, edit your code to populate 2 associative arrays and show us what you expect. Commented Sep 29, 2016 at 20:00
  • Solved: I finally found a SO question answering this problem stackoverflow.com/a/13306800/5324593 Commented Sep 29, 2016 at 20:15
  • Which version of bash? 4.3 has a new facility directly on-point. Commented Sep 29, 2016 at 20:22
  • BTW, BashFAQ #6 is directly on-point. Commented Sep 29, 2016 at 20:28
  • @ccarton, respectfully, the answer you linked to isn't appropriate for use with arrays, associative or otherwise. Commented Sep 29, 2016 at 20:29

1 Answer 1

3

The modern (bash 4.3+) approach uses "namevars", a facility borrowed from ksh:

for _count_var in "${!count_@}"; do
    declare -n count=$_count_var                  # make count an alias for $_count_var
    for key in "${!count[@]}"; do                 # iterate over keys, via same
        echo "$key occurred ${count[$key]} times" # extract value, likewise
    done
    unset -n count                                # clear that alias
done

declare -n count=$count_var allows "${count[foo]}" to be used to look up item foo in the associative array named count_var; similarly, count[foo]=bar will assign to that same item. unset -n count then removes this mapping.


Prior to bash 4.3:

for _count_var in "${!count_@}"; do
  printf -v cmd '_count_keys=( "${!%q[@]}" )' "$_count_var" && eval "$cmd"
  for key in "${_count_keys[@]}"; do
    var="$_count_var[$key]"
    echo "$key occurred ${!var} times"
  done
done

Note the use of %q, rather than substituting a variable name directly into a string, to generate the command to eval. Even though in this case we're probably safe (because the set of possible variable names is restricted), following this practice reduces the amount of context that needs to be considered to determine whether an indirect expansion is secure.


In both cases, note that internal variables (_count_var, _count_keys, etc) use names that don't match the count_* pattern.

Sign up to request clarification or add additional context in comments.

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.