I was reading this Q&A: How to loop over the lines of a file?
What is the IFS
variable? And what is its usage in the context of for
-loops?
I was reading this Q&A: How to loop over the lines of a file?
What is the IFS
variable? And what is its usage in the context of for
-loops?
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:
"$foo"
, or in a variable assignment x=$foo
, then the string resulting from the substitution is used as-is.$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).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
.
IFS
(directly) determines how the output of the command substitution is broken up, which then (indirectly) determines what the loop loops over.
– Gilles 'SO- stop being evil'
May 28 '18 at 18:58
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 the recommended one...
As @jasonwryan noticed, it's not Input
but Internal
. Name Input
came from awk
in which there is also OFS
- Output Field Separator
.
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
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.
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.