15

I'm having this code from http://bash.cyberciti.biz/guide/While_loop, used to read line by line from a file

file=/etc/resolv.conf
while IFS= read -r line
do
        # echo line is stored in $line
    echo $line
done < "$file"

the part I don't understand is IFS= and how it contributes to this functionality. Could anybody explain this to me? Thanks.

Kevin
  • 53,822
  • 15
  • 101
  • 132
wakandan
  • 1,099
  • 4
  • 19
  • 27

4 Answers4

44

In this case, IFS is set to the empty string to prevent read from stripping leading and trailing whitespace from the line.

Changing IFS is usually done to control how the input will be split into multiple fields. But in this case, because there's only one variable name given to read, read won't ever split the input into multiple fields regardless of the value of IFS. It will, however, remove the leading and trailing whitespace as mandated in the POSIX specification (assuming the value of IFS contains whitespace or is unset).

See the POSIX specification for read and field splitting for details about how it works.

Richard Hansen
  • 51,690
  • 20
  • 90
  • 97
4

In the third example on that page, setting IFS to null prevents word splitting which makes that code not work. Here is that code:

while IFS= read -r field1 field2 field3 ... fieldN
do
    command1 on $field1
    command2 on $field1 and $field3
    ..
    ....
    commandN on $field1 ... $fieldN
done < "/path/to dir/file name with space"

As written, all the words on the line are stored in field1 and field2, etc., are empty. Change the line to this and it will work properly:

while read -r field1 field2 field3 ... fieldN
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
-3

To make IFS a genuine line separator, use IFS=$'\012'.

gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
  • 2
    You didn't answer the question. The question was to explain the part `IFS=` in the command, and explain its use. And by the way, `IFS=$'\n'` would be (arguably) less cryptic. – gniourf_gniourf May 25 '14 at 12:46
  • Quite true. But that part has essentially been answered above, and these are the gory details. Perhaps this questions should be considered answered. – user3673660 May 25 '14 at 12:49
  • If the question has already been answered, then there's no point in adding answers that don't answer the question. Oh well, it's your choice; maybe you'll even get upvotes for that. Or not. Wait and see. – gniourf_gniourf May 25 '14 at 12:51
-4

IFS is a variable for the line separator (or actually "Internal Field Separator"). That code will effectively empty out the line separator for your read command and set it to its default. Sometimes IFS is changed somewhere else in the code due to users wanting other "line" endings, like reading one sentence at a time (IFS=.) or similar.

I guess they included the IFS= here just to make sure it works or everyone, regardless of the previous value on the IFS variable. The code should still work without IFS=

Emil Vikström
  • 90,431
  • 16
  • 141
  • 175
  • that's true, without `IFS=` it's still working fine. Thanks a lot. – wakandan Dec 08 '10 at 09:28
  • just one quick thing, we can put such an assignment `IFS=` and `read -r line` in a same line? It's allowed? – wakandan Dec 08 '10 at 09:31
  • 2
    user121870, yes, it's allowed. When putting variables in front of a command he variable will be visible to that command only. This applies to all Bash commands, even those you write in your terminal yourself. Try for example this command: "MANWIDTH=20 man bash". – Emil Vikström Dec 08 '10 at 09:35
  • 1
    Setting `IFS` to a null does not set it to its default (which is space, tab, newline). It's not for line endings only, it's used to do word splitting. To read one sentence at a time, you would use the `-d` delimiter option with `read`. – Dennis Williamson Dec 08 '10 at 10:53
  • 15
    This answer is incorrect -- `IFS` is set to the empty string to prevent `read` from stripping leading and trailing whitespace from each line. – Richard Hansen Jun 18 '11 at 23:34
  • 5
    IFS is not a line separator, it's a **field** separator. That means it's used to separate both lines and within lines. But in this case, since you're using `read` with a single output variable, it works. For example, if you want to parse /etc/resolv.conf quicker, you can use `while IFS=' ' read -r name address` to split the fields in the same operation. – l0b0 Jun 20 '11 at 12:22