← Back to Bash Course | Chapter 4: Loops | Lesson 14 of 14

Looping Over Command Output

Loops can process the output of another command line by line, which is one of the most common real-world patterns in shell scripting for handling logs, file listings, or any generated text.

The while read Idiom

while IFS= read -r line; do ... done <<< "$data" is the standard safe way to process text line by line: -r disables backslash interpretation, and IFS= stops leading/trailing whitespace from being stripped off each line. Feeding it via <<< (a here-string) keeps the loop running in the current shell rather than a subshell.

Example: The while read Idiom

bash
#!/bin/bash
data=$'line one\nline two\nline three'
while IFS= read -r line; do
    echo "Processing: $line"
done <<< "$data"

Reading a File Line by Line

The same while read idiom works directly against a file using input redirection (< file) instead of a here-string, without ever loading the whole file into a variable first. This scales well even for large files since each line is read one at a time.

Example: Reading a File Line by Line

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

Looping Over Command Output with Process Substitution

Feeding while read from a command's output via process substitution, < <(command), avoids the subshell problem that piping (command | while read) causes, since the loop still runs in the current shell. This means variables set inside the loop remain visible after it finishes.

Example: Looping Over Command Output with Process Substitution

bash
#!/bin/bash
total=0
while IFS= read -r num; do
    total=$((total + num))
done < <(printf "1\n2\n3\n")
echo "Total after loop: $total"

for word in $(command) for Simple Word Lists

When a command's output is known to be simple, whitespace-separated single-word tokens with no embedded spaces, for word in $(command); do ... done is a quick way to iterate over them. For anything line-structured or containing spaces, while read is the safer and more correct choice.

Warning: This pattern breaks on multi-word lines -- reach for while read whenever a line's meaning depends on keeping it whole.

Example: for word in $(command) for Simple Word Lists

bash
#!/bin/bash
for status_code in $(printf "200 404 500"); do
    echo "HTTP status: $status_code"
done
Common Mistakes
  1. Using for line in $(command) to loop over multi-word lines; this word-splits on every space and newline, mangling any line that contains more than one word -- while read is the correct tool for line-based processing.
  2. Piping into a while read loop and then being confused that variables set inside the loop disappear afterward; a loop that is the receiving end of a pipe runs in a subshell, so its variable changes don't persist in the parent shell (process substitution avoids this, but that's a more advanced fix).
  3. Forgetting read strips leading/trailing whitespace and interprets backslashes by default unless -r (raw mode) is used and IFS is adjusted -- omitting -r can silently corrupt lines containing backslashes.
Chapter Summary
  • while IFS= read -r line; do ... done <<< "$data" (or < file) is the standard, safe idiom for processing text line by line.
  • -r prevents read from interpreting backslash escapes; IFS= prevents it from trimming leading/trailing whitespace from each line.
  • Feeding a while read loop via a pipe puts it in a subshell, so variables it sets are lost after the loop; using <<< or < file for input instead keeps it in the current shell.
  • for word in $(command) should be reserved for simple, known-safe, single-word-per-item output -- for anything line-oriented, prefer while read.

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.