32

I have one file with a list of names. I need to loop through all names in this file from an external file with a shell script. How can I do that?

Example files:

scripts/names.txt

alison
barb
charlie
david

scripts/script.sh

NAMES="" #names from names.txt file
for NAME in $NAMES; do
    echo "$NAME"
done

How can I explode the names.txt file into an array in a separate shell script?

1
  • 1
    How about a link to the original Q&A? Commented Sep 28, 2015 at 16:17

5 Answers 5

62

One way would be:

while read NAME
do
    echo "$NAME"
done < names.txt

EDIT: Note that the loop gets executed in a sub-shell, so any modified variables will be local, except if you declare them with declare outside the loop.

Dennis Williamson is right. Sorry, must have used piped constructs too often and got confused.

Sign up to request clarification or add additional context in comments.

3 Comments

No, since nothing is being piped into while, there's no subshell. Redirection into done doesn't create a subshell.
By the way, the issue (bug?) with pipes does not occur with korn shell (but does with pdksh).
Someone should clarify that the file you're looping over should have an empty last line, otherwise the last element won't be considered
15

You'll be wanting to use the 'read' command

while read name
do
    echo "$name"
done < names.txt

Note that "$name" is quoted -- if it's not, it will be split using the characters in $IFS as delimiters. This probably won't be noticed if you're just echoing the variable, but if your file contains a list of file names which you want to copy, those will get broken down by $IFS if the variable is unquoted, which is not what you want or expect.

If you want to use Mike Clark's approach (loading into a variable rather than using read), you can do it without the use of cat:

NAMES="$(< scripts/names.txt)" #names from names.txt file
for NAME in $NAMES; do
    echo "$NAME"
done

The problem with this is that it loads the whole file into $NAMES, when you read it back out, you can either get the whole file (if quoted) or the file broken down by $IFS, if not quoted. By default, this will give you individual words, not individual lines. So if the name "Mary Jane" appeared on a line, you would get "Mary" and "Jane" as two separate names. Using read will get around this... although you could also change the value of $IFS

Comments

8
cat names.txt|while read line; do
    echo "$line";
done

1 Comment

while read line; do ...; done < names.txt
5

I know the purists will hate this method, but you can cat the file.

NAMES=`cat scripts/names.txt` #names from names.txt file
for NAME in $NAMES; do
   echo "$NAME"
done

2 Comments

This has the advantage of not invoking a sub-shell, but will consider any space in names.txt as a delimiter.
The "purists" will helpfully explain what's wrong with this, too. There are multiple issues. See mywiki.wooledge.org/DontReadLinesWithFor
2

This might work for you:

cat <<\! >names.txt
> alison
> barb
> charlie
> david
> !
OIFS=$IFS; IFS=$'\n'; NAMES=($(<names.txt)); IFS=$OIFS
echo "${NAMES[@]}"
alison barb charlie david
echo "${NAMES[0]}"
alison
for NAME in "${NAMES[@]}";do echo $NAME;done
alison
barb
charlie
david

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.