Skip to content
dunatotatos edited this page Sep 18, 2016 · 11 revisions

To read lines rather than words, pipe/redirect to a 'while read' loop.

Problematic code:

for line in $(cat file | grep -v '^ *#')
do
  echo "Line: $line"
done

Correct code:

grep -v '^ *#' < file | while IFS= read -r line
do
  echo "Line: $line"
done

or without a subshell (bash, zsh, ksh):

while IFS= read -r line
do
  echo "Line: $line"
done < <(grep -v '^ *#' < file)

or without a subshell, with a pipe (more portable, but write a file on the filesystem):

mkfifo mypipe
grep -v '^ *#' < file > mypipe &
while IFS= read -r line
do
  echo "Line: $line"
done < mypipe
rm mypipe

Rationale:

For loops by default (subject to $IFS) read word by word. Additionally, glob expansion will occur.

Given this text file:

foo *
bar

The for loop will print:

Line: foo
Line: aardwark.jpg
Line: bullfrog.jpg
...

The while loop will print:

Line: foo *
Line: bar

Exceptions

If you want to read word by word, you should still use a while read loop (e.g. with read -a to read words into an array).

Rare reasons for ignoring this message is if you don't care because your file only contains numbers and you're not interested in good practices, or if you've set $IFS appropriately and also disabled globbing.

ShellCheck

Each individual ShellCheck warning has its own wiki page like SC1000. Use GitHub Wiki's "Pages" feature above to find a specific one, or see Checks.

Clone this wiki locally