4

I'm learning bash and I found a tutorial on internet that says these are the same:

while read -r line;
do
    ...
done < file

$ cat file | while IFS= read -r line;
do
    ...
done

Are there any subtle differences in these two loops are are they really the same?

bodacydo
  • 75,521
  • 93
  • 229
  • 319

2 Answers2

9

The biggest difference is that in the pipeline, the while loop executes in a subshell, so if you change the values of any variables in the body of the while, those will be lost after the pipeline completes.

$ foo=5
$ cat file | while IFS= read -r line; do
>    foo=$line  # assume $line is not 5
> done
$ echo $foo
5
$ while IFS= read -r line; do
>  foo=$line
> done < file  # Assume one line with the word foo
$ echo $foo
foo

In bash 4.2, this can be mitigated by using the lastpipe option, which allows the last command in a pipeline to be executed in the current shell instead of a subshell.

Aside from that, the version using input redirection is more efficient, since it does not require extra processes to be started.

chepner
  • 497,756
  • 71
  • 530
  • 681
1

In addition to chepner's observation about subshells, one of the loops uses IFS= and one does not.

read uses this variable to split up words. With one variable, this affects leading and trailing whitespace.

With IFS=, it's preserved:

$ IFS= read -r line <<< "   test   "
$ printf "<%s>\n" "$line"
<   test   >

Otherwise, it's stripped:

$ read -r line <<< "   test   "
$ printf "<%s>\n" "$line"
<test>

You can imagine how much havoc the first non-IFS= loop would wreck on e.g. a Python file.

that other guy
  • 116,971
  • 11
  • 170
  • 194