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.

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

Comments

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 Comments

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

can you explain a bit what does this eval do?

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.