11

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.

2
  • What do you define as "not working"? I get 4 lines of output, in the expected order. There are issues with your approach, but with the sample data, it seems to work. Commented Jan 31, 2017 at 19:43
  • Related: multi-dimensional arrays in BASH Commented Jan 31, 2017 at 19:44

2 Answers 2

16

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
Sign up to request clarification or add additional context in comments.

Comments

9

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!

Comments

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.