2

Trying to write some nested loop, and I'm not getting how to write it. Perhaps I'm looking in a wrong direction but what I'm trying to write is:

declare -a bar=("alpha" "bravo" "charlie")
declare -a foo=("delta" "echo" "foxtrot" "golf")

declare -a subgroups=("bar" "foo")

So then I would like to iterate the subgroups (in the future more bars and foos will come), and inside them iterate them as they can have a different number of elements.

The desired output would be something like:

group name: bar with group members: alpha bravo charlie
        working on alpha of the bar group
        working on bravo of the bar group
        working on charlie of the bar group
group name: foo with group members: delta echo foxtrot golf
        working on delta of the foo group
        working on echo of the foo group
        working on foxtrot of the foo group
        working on golf of the foo group

The closes code I've wrote seems fail in the bar and foo arrays and its expansion with the elements on each set.

for group in "${subgroups[@]}"; do
   lst=${!group}
   echo "group name: ${group} with group members: ${!lst[@]}"
   for element in "${!lst[@]}"; do
       echo -en "\tworking on $element of the $group group\n"
   done
done

And the output is:

group name: bar with group members: 0
        working on 0 of the bar group
group name: foo with group members: 0
        working on 0 of the foo group
3

This is a pretty common problem in bash, to reference array within arrays for which you need to create name-references with declare -n. The name following the -n will act as a nameref to the value assigned (after =). Now we treat this variable with nameref attribute to expand as if it were an array and do a full proper quoted array expansion as before.

for group in "${subgroups[@]}"; do
    declare -n lst="$group"
    echo "group name: ${group} with group members: ${lst[@]}"
    for element in "${lst[@]}"; do
        echo -en "\tworking on $element of the $group group\n"
    done
done

Note that bash supports nameref's from v4.3 onwards only. For older versions and other workarounds see Assigning indirect/reference variables

  • Might be worth clarifying that the ! with an array is supposed to be key syntax, and doesnt work as a name-ref the way regular variables do – D. Ben Knoble Oct 7 '19 at 12:40
1

The minimum changes to make your script work correctly, it becomes:

#!/bin/bash

declare -a bar=("alpha" "bravo" "charlie")
declare -a foo=("delta" "echo" "foxtrot" "golf")

declare -a groups=("bar" "foo")

for group in "${groups[@]}"; do
    lst="$group[@]"
    echo "group name: ${group} with group members: ${!lst}"
    for element in "${!lst}"; do
        echo -en "\tworking on $element of the $group group\n"
    done
done

The two main changes are:

  • Don't use "${!lst[@]}", not even "${!group[@]}" to access array elements. This syntax is only to access array indexes.

    Do use ${!lst}".

  • The variable lst should be set to contain the string that you would have written inside a normal ${ }, that is: lst=foo[@] on first level and to lst="$group[@]" if you need that the name of the array is also indirect via the value of variable group.

    lst="$group[@]"
    

An equivalent syntax with namerefs has no ! (nor it needs) to expand values.
As a consequence it needs the [@] removed above.

echo "using namerefs"

for group in "${groups[@]}"; do
    declare -n lst=$group
    echo "group name: ${group} with group members: ${lst[@]}"
    for element in "${lst[@]}"; do
        echo -en "\tworking on $element of the $group group\n"
    done
done

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.