Iterate over lines instead of words in a for loop of shell script

The for loop is not designed to loop over “lines”. Instead it loops over “words”.

Short simplified terminology: “lines” are things separated by newlines. “words” are things separated by spaces. in bash lingo “words” are called “fields”.

The idiomatic way to loop over lines is to use a while loop in combination with read.

ioscan -m dsf | while read -r line
do
  printf '%s\n' "$line"
done

Note that the while loop is in a subshell because of the pipe. This can cause some confusion with variable scope. In bash you can work around this by using process substitution.

while read -r line
do
  printf '%s\n' "$line"
done < <(ioscan -m dsf)

But now the “generator” (ioscan in this example) is in a subshell.

For more information about the subshell problematic in loops see http://mywiki.wooledge.org/BashFAQ/024


Often you will see the suggestion to change the value of $IFS to only newline. IFS is short for Internal Field Separator. Usually $IFS contains a space, a tab, and a newline.

Here is the typical way to do so:

OLDIFS="$IFS"
IFS=$'\n' # bash specific
for line in $(ioscan -m dsf)
do
  printf '%s\n' "$line"
done
IFS="$OLDIFS"

(the bash specific part ($'\n') is called ANSI-C Quoting)

But beware many commands depends on some sane setting for $IFS. I do not recommend changing $IFS. Too often it will cause an endless nightmare of obscure bug hunting.


See also:

  • http://wiki.bash-hackers.org/syntax/ccmd/classic_for
  • http://wiki.bash-hackers.org/commands/builtin/read
  • http://mywiki.wooledge.org/IFS
  • http://mywiki.wooledge.org/SubShell
  • http://mywiki.wooledge.org/ProcessSubstitution

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)