9

I want to write a script to reference multiple arrays from another array which holds these array's variable names.

Here's my code so far:

#!/bin/bash array1=('array1string1' 'array1string2') array2=('array2string1' 'array2string2') array_names=('array1' 'array2') for a in ${array_names[@]} do for b in ${a[@]} do echo $b done done 

I'd like the output to scan through both arrays (from the outer for loop) and print the respective strings in the inner for loop which calls echo. My current output is just showing me:

array1 array2 

I'd be grateful for any pointers regarding this. Thank you!

5
  • Is there any reason why you can't just do for b in "${array1[@]}" "${array2[@]}"; do ...; done?
    – Kusalananda
    CommentedSep 6, 2017 at 17:14
  • I'd like the number of arrays to be flexible. Therefore, if I add an array later, I would just add it to array_names and let the loop take care of it.
    – chnppp
    CommentedSep 6, 2017 at 17:22
  • I think this is a case for indirect expansion. See stackoverflow.com/questions/8515411/… - but, basically, changing ${a[@]} to ${!a} does what you want (I think).
    – parkamark
    CommentedSep 6, 2017 at 17:24
  • 1
    @parkamark No, that just gives him the first element of each array. And ${!a[@]} gives a length of the array a.
    – Kusalananda
    CommentedSep 6, 2017 at 17:27
  • Yes, changing to ${!a} is just giving me the first elements.
    – chnppp
    CommentedSep 6, 2017 at 17:29

3 Answers 3

9

Bash 4.3 and later supports "name references", or namerefs (a similar concept exists in ksh93, but the scoping is annoyingly different):

#!/bin/bash array1=('array1string1' 'array1string2') array2=('array2string1' 'array2string2') array_names=('array1' 'array2') for a in "${array_names[@]}"; do declare -n arr="$a" for b in "${arr[@]}"; do echo "$b" done done 

The variable arr is a nameref that acts like an alias for the named variable (the variable with name $a in this example).

Without namerefs, in earlier Bash versions, one solution would be to create a new array that contains all the elements from the other arrays:

all=( "${array1[@]}" "${array2[@]}" ) 

... a bit like the array_names array in the question but with the contents of all arrays, and then iterate over "${all[@]}".

It's also possible to use eval, but the resulting code looks astoundingly awful.

See glenn jackman's answer for a variation with variable indirection (introduced in its current form with Bash version 2).

    2

    @Kusalananda has the best answer for recent versions of bash. For earlier versions, you can use an indirect variable:

    for a in ${array_names[@]}; do tmp="${a}[@]" for b in "${!tmp}"; do echo "$b"; done # or: printf "%s\n" "${!tmp}" done 

    See the 4th paragraph of https://www.gnu.org/software/bash/manual/bashref.html#Shell-Parameter-Expansion

      0

      As a variation on what's been said:

      #!/bin/bash array1=('array1 string1' 'array1 string2') array2=('array2 string1' 'array2 string2') array_names=('array1' 'array2') for (( i=0; i<${#array_names[@]}; i++ )); do declare -n arr="${array_names[i]}" for (( j=0; j<${#arr[@]}; j++ )); do echo "${arr[j]}" done done 

      Accessing the elements by index instead

        You must log in to answer this question.

        Start asking to get answers

        Find the answer to your question by asking.

        Ask question

        Explore related questions

        See similar questions with these tags.