← Back to Bash Course | Chapter 9: File Testing & Filesystem | Lesson 6 of 13

Reading a File Line by Line

You can make a script go through a text file one line at a time, running some logic on each line as it goes.

The Standard while-read Idiom

Redirecting a file into a while read loop with < file after done reads one line per iteration until end-of-file, without needing a subshell. IFS= read -r preserves whitespace and disables backslash escaping.

Example: The Standard while-read Idiom

bash
#!/bin/bash
printf "apple\nbanana\ncherry\n" > fruits.txt
while IFS= read -r line; do
    echo "Fruit: $line"
done < fruits.txt
rm -f fruits.txt

Why Piping Loses Variables

Piping a command into while read, like cat file | while read line; do ...; done, runs the loop in a subshell because it is on the right side of a pipe. Any variables changed inside that loop disappear once the loop finishes.

Warning: If you need to accumulate state while reading a file, use < file redirection, not a pipe, into the while loop.

Example: Why Piping Loses Variables

bash
#!/bin/bash
printf "1\n2\n3\n" > numbers.txt
count=0
cat numbers.txt | while read -r n; do
    count=$((count + 1))
done
echo "count after piped loop (lost): $count"

count=0
while read -r n; do
    count=$((count + 1))
done < numbers.txt
echo "count after redirected loop (kept): $count"
rm -f numbers.txt

Reading the Last Line Without a Trailing Newline

If a file's last line has no trailing newline, plain read still succeeds in returning that final line's text but returns a non-zero status, which can cause the loop body to be skipped. Checking || [[ -n $line ]] handles that edge case.

Example: Reading the Last Line Without a Trailing Newline

bash
#!/bin/bash
printf "one\ntwo\nthree" > no_trailing_newline.txt
while IFS= read -r line || [[ -n "$line" ]]; do
    echo "Got: $line"
done < no_trailing_newline.txt
rm -f no_trailing_newline.txt
Common Mistakes
  1. Piping into a while read loop and then wondering why variables set inside the loop don't exist after it; a piped loop runs in a subshell.
  2. Forgetting IFS= before read, which trims leading/trailing whitespace from each line unexpectedly.
  3. Using for line in $(cat file) instead of while read; the for version splits on whitespace and breaks on lines containing spaces.
Chapter Summary
  • while IFS= read -r line; do ... done < file is the standard, safe idiom for reading a file line by line.
  • Redirecting the file with < file after done keeps the loop in the current shell, unlike piping into it.
  • IFS= prevents leading/trailing whitespace from being stripped from each line.
  • read returns non-zero on end-of-file, which naturally ends the while loop.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.