Celi Manu
Celi Manu

Reputation: 471

How can I make an array of lists (or similar) in bash?

I want to iterate over a few lists in bash. Right now I have

array=("list1item1 list1item2" "list2item list2item2")

for list in "${array[@]}"
do
    for item in $list
    do
        echo $item
    done
done

This doesn't work. Is there any way to make a list of lists, array of arrays, or array of lists in bash?

I want to iterate over list1, then the listitems in list1. Then iterate over list2, and the list items in list2.

Upvotes: 10

Views: 17611

Answers (2)

methuselah-0
methuselah-0

Reputation: 124

lacking reputation to comment on rici...

It is now possible to create list of lists, more or less, and avoid using namereferences for this as in rici's answer(from bash4.4 and onwards), thanks to array quote expansion: @Q. For example:

declare -a list1=("one" "two three")
declare -a list2=("four five" "six")
declare -a listOfLists=("(${list1[*]@Q})" "(${list2[*]@Q})")
echo "${#listOfLists[@]}"
2

As you can see the listOfLists expands correctly to 2 elements. Now the nice thing is that thanks to @Q, the elements inside listOfLists which are lists, will also expand correctly to 2 elements each (instead of the 3 elements it would have without using @Q):

declare -a sameAsList1="${listOfLists[0]}"; declare -a sameAsList2="${listOfLists[1]}"
echo "${#sameAsList1[@]}" ; echo "${#sameAsList2[@]}"
2
2
declare -p sameAsList1 && declare -p list1
declare -a sameAsList1=([0]="one" [1]="two three")
declare -a list1=([0]="one" [1]="two three")

We got list of lists, finally!

Upvotes: 7

rici
rici

Reputation: 241711

Once I added the missing do and done into your code:

array=("list1item1 list1item2" "list2item list2item2")

for list in "${array[@]}"
do
    for item in $list
    do
        echo $item
    done
done

it produced the expected output:

list1item1
list1item2
list2item
list2item2

It's not clear to me how this differs from your expectation.

However, that is not a very general way of nesting a list into an array, since it depends on the internal list being IFS-separated. Bash does not offer nested arrays; an array is strictly an array of strings, and nothing else.

You can use indirection (${!v}) and store variable names into your outer array, although it is a bit ugly. A less ugly variant is the following, which relies on namerefs; it will work with reasonably recent bash versions:

array=(list1 list2)
list1=("list 1 item 1" "list 1 item 2")
list2=("list 2 item 1" "list 2 item 2")
for name in "${array[@]}"; do
  declare -n list=$name
  for item in ${list[@]}; do
    echo "$item"
  done
done

Output:

list 1 item 1
list 1 item 2
list 2 item 1
list 2 item 2

Upvotes: 15

Related Questions