In bash I frequently make scripts where I loop over a list of strings that I define.
e.g.
for a in 1 2 3 4; do echo $a; done
However I would like to define the list (before the loop to keep it clean) so that it contains spaces and with out a separate file:
e.g. (BUT THIS WILL NOT WORK)
read -r VAR <<HERE
list item 1
list item 2
list item 3
...
HERE
for a in $VAR; do echo $a; done
The expected output above (I would like):
list item 1
list item 2
list item 3
etc...
But you will get:
list
item
1
I could use arrays but I would have to index each element in the array (EDIT read answers below as you can append to arrays.. I did not know you could).
How do others declaratively define lists in bash with out using separate files?
Sorry I forgot to mention I want to define the list at the top of the file before the for loop logic
Arrays aren't so hard to use:
readarray <<HERE
this is my first line
this is my second line
this is my third line
HERE
# Pre bash-4, you would need to build the array more explicity
# Just like readarray defaults to MAPFILE, so read defaults to REPLY
# Tip o' the hat to Dennis Williamson for pointing out that arrays
# are easily appended to.
# while read ; do
# MAPFILE+=("$REPLY")
# done
for a in "${MAPFILE[@]}"; do
echo "$a"
done
This has the added benefit of allowing each list item to contain spaces, should you have that need.
You can use the "HERE Document" like this:
while read a ; do echo "Line: $a" ; done <<HERE
123 ab c
def aldkfgjlaskdjf lkajsdlfkjlasdjf
asl;kdfj ;laksjdf;lkj asd;lf sdpf -aa8
HERE
while read -r line
do
var+=$line$'\n'
done <<EOF
foo bar
baz qux
EOF
while read -r line
do
echo "[$line]"
done <<<"$var"
Why would you need to index arrays? You can append to arrays and iterate over them without using indices.
array+=(value)
for item in "${array[@]}"
do
something with "$item"
done
There are better answers here, but you can also delimit the read on \n
and temporarily change the variable to split on newlines instead of whitespace in the for
loop using the IFS
environment variable.
read -d \n -r VAR <<HERE
list item 1
list item 2
list item 3
HERE
IFS_BAK=$IFS
IFS="\n"
for a in $VAR; do echo $a; done
IFS=$IFS_BAK
When it's fine for you to use a while
loop instead of a for
loop, you can make use of the while read
construct and a "here document":
#!/bin/bash
while read LINE; do
echo "${LINE}"
done << EOF
list item 1
list item 2
list item 3
EOF
ref: How does ` cat << EOF` work in bash?