0

I've noticed that to print the value of the $IFS variable in the shell I have to do something like:

$ printf '%s\nYour IFS is: %q' 'Hello' "$IFS"
Hello
Your IFS is: $' \t\n'

My question is why do I need to pass the IFS in that special way? For example, why (or why wouldn't) it be possible to do:

  • $ echo $IFS -- some parameter that prints special characters?
  • $ printf "$IFS" or $ printf '$IFS' -- why wouldn't either of these work?
  • Why $ printf "%q" $IFS and $ printf "%q" '$IFS' don't show this properly but $ printf "%q" "$IFS" does?
Inian
  • 80,270
  • 14
  • 142
  • 161
carl.hiass
  • 1,526
  • 1
  • 6
  • 26
  • This depends on what you set your IFS to. Like with any printable string, you can display it by `echo "$IFS"` (of course, you need to quote it). Try (in a subshell) `IFS=Z; echo "$IFS"; exit`, and you will see an `x` displayed. But if you have a variable holding non-printable characters, you won't get much interesting output. This is the same for IFS and anything else. – user1934428 Sep 04 '20 at 09:16

3 Answers3

2
  • $ echo $IFS -- some parameter that prints special characters?

echo doesn't have such a parameter

  • $ printf "$IFS" or $ printf '$IFS' -- why wouldn't either of these work?
  1. The first does interpolation of the string just like echo does and prints the IFS string just like it is - which by default is a bunch of whitespaces.
  2. The second does not do interpolation and obviously prints $IFS.
  • printf "%q" $IFS

The variable has already been expanded into whitespaces that are eaten up by the shell so nothing is passed as a second parameter to printf and therefore %q has no input to work with.

  • printf "%q" '$IFS'

The string $IFS is passed as a parameter to %q which just adds escape characters to it.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
2

You are running into three basic problems:

  • Unquoted variable references (as in echo $IFS and printf "%q" $IFS) undergo word splitting. Basically, any whitespace in the variable's value is treated as a separator between "words" (which get passed as arguments to the command). But "whitespace" is defined as the characters in $IFS (that's what it's for!). Therefore, the entire variable's value gets treated as just spacing, not actual content, and effectively vanishes!

    This is one of the many examples of why you should put double-quotes around variable references.

  • Single-quoted strings (as in printf '$IFS' and printf "%q" '$IFS') don't undergo variable expansion at all. In these cases, $IFS is just a literal string, not a variable reference.

  • Finally, the default characters in $IFS aren't particularly visible on screen. printf "$IFS" actually does print them correctly (unless you put "%" or "\" in IFS for some reason), but you can't really see them. By default, the first character in $IFS is a space, so when that prints the cursor moves to the second column, but nothing visible appears. The second character is a tab, so the cursor moves even further over, but again nothing is actually visible. Then the last character is a newline, so the cursor moves to the beginning of the next line... and again, nothing visible appears.

    This is why printf %q is needed -- the %q format converts the not-really-visible characters in $IFS into a visible, readable representation.

Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
1

If you don't quote the variable, word-splitting is done after the variable is expanded. Word splitting treats all the characters in IFS as word delimiters; multiple of them in a row between words are collapsed into a single space, and if the output is entirely delimiters, nothing at all is output.

You need to use quotes around the variable to output it literally.

Then you need to use the %q format operator to output it as escape sequences so you can see what the individual characters are. Otherwise you wouldn't be able to tell that the second character is a TAB, you would just see a bunch of spaces on the screen. And the newline would just go to the next line.

Barmar
  • 741,623
  • 53
  • 500
  • 612