Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

arrays - Bash: Iterate over variable names

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
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...