2

I have an array containing names of categories

categories=( categoryA categoryB categoryC )

Each category contains multiple products, which is stored in different arrays.

categoryA=( productA1 productA2 productA3 )
categoryB=( productB1 productB2 productB3 )
categoryC=( productC1 productC2 productC3 )

I want to loop over all products, so I wrote this code:

categories=( categoryA categoryB categoryC )

categoryA=( productA1 productA2 productA3 )
categoryB=( productB1 productB2 productB3 )
categoryC=( productC1 productC2 productC3 )

for category_name in "${categories[@]}"
do
    echo $category_name

    category=${!category_name}
    for product in "${category[@]}" 
    do
        echo -e '\t' $product
    done

    echo -e '\n'

done

I expected the output to be:

CategoryA
    ProductA1  
    ProductA2
    ProductA3

CategoryB
    ProductB1  
    ProductB2
    ProductB3

CategoryC
    ProductC1  
    ProductC2
    ProductC3

but unfortunately, the output is

CategoryA
    ProductA1  

CategoryB
    ProductB1  

CategoryC
    ProductC1  

How to fix this ?

3 Answers 3

1

If Inian's answer doesn't work for you because your are using a version of bash older than 4.3 (where the support for nameref variables was introduced), you can go for the following solution:

categories=( categoryA categoryB categoryC )

categoryA=( productA1 productA2 productA3 )
categoryB=( productB1 productB2 productB3 )
categoryC=( productC1 productC2 productC3 )

for category_name in "${categories[@]}"
do
    echo $category_name


    array_declaration="$(declare -p $category_name)"
    # "$array_declaration" expands to the command that can be (safely)
    # evaluated to recreate the $category_name variable 

    # change the variable name in the array declaration and eval it
    eval "${array_declaration/#declare -a "$category_name"=/declare -a category=}"

    for product in "${category[@]}"
    do
        echo -e '\t' $product
    done

    echo -e '\n'

done

Note that reliance on eval in the above code should be safe, since the output of declare -p is properly quoted.

1

Absolutely do NOT use eval for this case, just use declare to create variables on the fly,

Just replace the line

category=${!category_name}

in your original answer with

declare -n category="${category_name}"

the above declare syntax will create a reference for category to the variable category_name which can be accessed further for expanding as an array.


declare -n is a relatively newer syntax available since introduced in Bash 4.3-alpha

4
  • I am getting the following error with this solution: main.sh: line 11: declare: -n: invalid option declare: usage: declare [-aAfFgilrtux] [-p] [name[=value] ...] Commented Mar 29, 2017 at 11:27
  • @AshrafBashir: Refer my update in the answer, it needs a version 4.3 or greater.
    – Inian
    Commented Mar 29, 2017 at 11:27
  • Ah ok, thanks, after the update I got the point, what about < 4.3 is there an alternative to eval ? Commented Mar 29, 2017 at 11:27
  • @AshrafBashir: eval is not recommended to use for safety considerations. If you don't have any maintainability problems, suggest upgrading your bash version
    – Inian
    Commented Mar 29, 2017 at 11:29
0

I already found a solution,

instead of category=${!category_name} ,
I can eval the array eval category=\( \${${category_name}[@]} \)

for those who are interested, here it is:

for category_name in "${categories[@]}"
do
    echo $category_name

    eval category=\( \${${category_name}[@]} \)
    for product in "${category[@]}" 
    do
        echo -e '\t' $product
    done

    echo -e '\n'

done

I will leave the question and the answer, for whoever meets the same challenge, and for anyone who has a more elegant solution, or for any objections


UPDATE: For security considerations, check Inian's response, and if you use Bash < 4.3 as I do, check Leon's response as an alternative

1
  • can you explain a bit what does this eval do?
    – Ashish K
    Commented Mar 29, 2017 at 11:25

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.