I was reading this thread: How to loop over the lines of a file?

What is IFS? And what is its usage in context of for-loops?

up vote 15 down vote accepted

IFS stands for Input Internal Field Separator - it's a character that separates fields. In the example you posted, it is set to new line character (\n); so after you set it, for will process text line by line. In that example, you could change the value of IFS (to some letter that you have in your input file) and check how text will be split.

BTW, from the answer you posted the second solution is that recommended...

As @jasonwryan noticed, it's not Input but Internal. Name Input came from awk in which there is also OFS - Output Field Separator.

  • 1
    Note that IFS is also used for combining parameters for output. From the bash man page: "When the expansion occurs within double quotes, it expands to a single word with the value of each parameter separated by the first character of the IFS special variable." – Eric Nov 30 '15 at 16:25
  • One should also note that while S was for separator in the Bourne shell where it originated from. In the Korn shell and most other Bourne-like shell as that has now been specified by POSIX, the S is for Delimiter (just stretch your imagination a bit) as A::B: for instance is split into "A", "" and "B" (no extra ""). That's different from awk's FS or the s parameter expansion flag of zsh. – Stéphane Chazelas Mar 13 '17 at 10:15

IFS isn't directly related to looping, it's related to word splitting. IFS indirectly determines how the output from the command is broken up into pieces that the loop iterates over.

When you have an unprotected variable substitution $foo or command substitution $(foo), there are two cases:

  • If the context expects a single word, e.g. when the substitution is between double quotes "$foo", or in a variable assignment x=$foo, then the string resulting from the substitution is used as-is.
  • If the context expects multiple words, which is the case most of the times, then two further expansions are performed on the resulting string:
    • The string is split into words. Any character that appears in $IFS is considered a word separator. For example IFS=":"; foo="12:34::78"; echo $foo prints 12 34 ​ 78 (with two spaces between 34 and 78, since there's an empty word).
    • Each word is treated as a glob pattern and expanded into a list of file names. For example, foo="*"; echo $foo prints the list of files in the current directory.

For loops, like many other contexts, expect a list of words. So

for x in $(foo); do …

breaks $(foo) into words, and treats each word as a glob pattern. The default value of IFS is space, tab and newline, so if foo prints out two lines hello world and howdy then the loop body is executed with x=hello, then x=world and x=howdy. If IFS is explicitly changed to contain a newline only, then the loop is executed for hello world and howdy. If IFS is changed to be o, then the loop is executed for hell, ​ w, rld​␤h (where ​␤ is a newline character) and wdy.

  • Why you mentioned "Indirectly" in "IFS indirectly determines how ..."? – devWeek May 28 at 14:43
  • 1
    @αғsнιη IFS (directly) determines how the output of the command substitution is broken up, which then (indirectly) determines what the loop loops over. – Gilles May 28 at 18:58

From man bash

IFS The Internal Field Separator that is used for word splitting after expansion and to split lines into words with the read builtin command. The default value is "<space><tab><newline>".

This is one of Bash's internal variables. It determines how Bash recognizes fields, or word boundaries, when it interprets character strings.

While it defaults to whitespace (space, tab, and newline), it may be changed, for example, to parse a comma-separated data file.

http://tldp.org/LDP/abs/html/internalvariables.html

In addition to previous great answers, let me just add that IFS is very useful for efficient and portable parsing in simple cases in combination with set. Efficient, because avoids using of subshells and spawning tools like grep or sed:

resolutions="640x480,320x240"
xIFS=$IFS
IFS=','
for res in $resolutions; do
    xxIFS=$IFS
    IFS='x'
    set -- $res
    width=$1
    height=$2
    # handle width and height
    IFS=$xxIFS
done;
IFS=$xIFS

Just note that we need to store and recover previous value of IFS to avoid undesired breakages in other parts of script.

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.