4

My closest most helpful matches when I searched for an answer ahead of posting:

Iterate over array in shell whose name is stored in a variable

How to use an argument/parameter name as a variable in a bash script

How to iterate over an array using indirect reference?

My attempt with partial success:

#!/bin/bash

declare -a large_furry_mammals
declare -a array_reference  
# I tried both declaring array_reference as an array and 
# not declaring it as an array.  no change in behavior.

large_furry_mammals=(horse zebra gorilla)


size=large
category=mammals

tmp="${size}_furry_${category}"
eval array_reference='$'$tmp

echo tmp=$tmp
echo array_reference[0]=${array_reference[0]}
echo array_reference[1]=${array_reference[1]}

Output

tmp=large_furry_mammals
array_reference[0]=horse
array_reference[1]=

Expectation

I would have expected to get the output zebra when I echoed array_reference[1].

...but I'm missing some subtlety...

Why can I not access elements of the index array beyond index 0? This suggests that array_reference is not actually being treated as an array.

I'm not looking to make a copy of the array. I want to reference (what will be) a static array based on a variable pointing to that array, i.e., ${size}_furry_${category} -> large_furry_mammals.

I've been successful with the general idea here using the links I've posted but only as long as its not an array. When it's an array, it's falling down for me.

Addendum Dec 5, 2018

bash 4.3 is not available in this case. @benjamin's answer does work on under 4.3.

I'll be needing to loop over the resulting array variable's contents. This kinda dumb example I gave involving mammals was just to describe the concept. There's actually a real world case around this. I have set of static reference arrays and an input string would be parsed to select which array was relevant and then I will loop over the array that was selected. I could do a case statement but with more than 100 reference arrays that would be the direct but overly verbose way to do it.

This pseudo code is probably better example of what I'm going after.

m1_array=(x a r d)
m2_array=(q 3 fg d)
m3_array=(c e p)

Based on some logic...select which array prefix you need.
x=m1

for each element in ${x}_array
do
   some-task
done

I'm doing some testing with @eduardo's solution to see if I can adapt the way he references the variables to get to my endgame.

** Addendum #2 December 14, 2018 **

Solution

I found it! Working with @eduardo's example I came up with the following:

#!/bin/bash

declare -a large_furry_mammals
#declare -a array_reference

large_furry_mammals=(horse zebra gorilla)


size=large
category=mammals

tmp="${size}_furry_${category}[@]"

for element in "${!tmp}"
do
    echo $element
done

Here is what execution looks like. We successfully iterate over the elements of the array string that was built dynamically.

./example3b.sh

horse
zebra
gorilla

Thank you everyone.

Pytheas
  • 43
  • 5
  • 2
    Whenever you want to do something indirectly, you should always first try to do it directly. It is not possible to add indirection to something that doesn't already work. See [this question](https://stackoverflow.com/questions/17735242/copy-a-bash-array-with-empty-elements) for how to copy arrays – that other guy Nov 30 '18 at 23:21

2 Answers2

2

If you have Bash 4.3 or newer, you can use namerefs:

large_furry_mammals=(horse zebra gorilla)
size=large
category=mammals
declare -n array_reference=${size}_furry_$category
printf '%s\n' "${array_reference[@]}"

with output

horse
zebra
gorilla

This is a reference, so changes are reflected in both large_furry_mammals and array_reference:

$ array_reference[0]='donkey'
$ large_furry_mammals[3]='llama'
$ printf '%s\n' "${array_reference[@]}"
donkey
zebra
gorilla
llama
$ printf '%s\n' "${large_furry_mammals[@]}"
donkey
zebra
gorilla
llama
Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
  • This solution does work under bash 4.3. I'm limited to an older version but wanted to acknowledge that its a solid solution. Thank you for taking the time to answer. – Pytheas Dec 05 '18 at 22:02
1
declare -a large_furry_mammals
declare -a array_reference
large_furry_mammals=(horse zebra gorilla)

size=large
category=mammals

echo ${large_furry_mammals[@]}
tmp="${size}_furry_${category}"
array_reference=${tmp}"[1]"
eval ${array_reference}='bear'
echo tmp=$tmp
echo ${large_furry_mammals[@]}
Eduardo
  • 7,631
  • 2
  • 30
  • 31
  • `array_reference` will be a copy, though. If you modify `array_reference[0]`, `large_furry_mammals[0]` will be unchanged. – Benjamin W. Nov 30 '18 at 23:35
  • ok, updated, note now how it changes zebra to bear, but you will see that I'm doing the eval on the array item 1, so I'd have to spend more time to give a full example. I have to leave now, but at least it shows you one way that works as a pointer. – Eduardo Dec 01 '18 at 02:18
  • I think this is going to be the solution that I can adapt best for my use case. Looking at it now. I'll update in a day or so after I've tested it in various ways. Thank you @Eduardo. – Pytheas Dec 05 '18 at 22:04
  • It is an interesting exercise, it's just really annoying. I'll see if later tonight or tomorrow I try a cleaner solution. – Eduardo Dec 05 '18 at 22:54
  • 1
    @Eduardo - I worked with your example and got to a solution. Solution added to my original post. Short story: I needed to add [@] to my array assignment statement. My original attempt consisted of constructing the the array name without the brackets - and then using the brackets on the backend when attempting to reference the array elements. The array indirect reference works if I include the [@] in the initial assignment. While your example was not a direct match for what I was doing it got me started in the right direction. – Pytheas Dec 14 '18 at 17:42
  • Great, I've been super busy, glad you found the final solution yourself. – Eduardo Dec 14 '18 at 18:48