Bash declaratively defining a list to loop on

2020-06-23 09:21发布

问题:

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

回答1:

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.



回答2:

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


回答3:

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


回答4:

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


回答5:

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?



标签: bash loops