Reputation: 471
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
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
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