In Bash, word splitting is a step in command line processing. From Bash Manual

The shell treats each character of $IFS as a delimiter, and splits the results of the other expansions into words using these characters as field terminators. If IFS is unset, or its value is exactly <space><tab><newline>, the default, then sequences of <space>, <tab>, and <newline> at the beginning and end of the results of the previous expansions are ignored, and any sequence of IFS characters not at the beginning or end serves to delimit words. If IFS has a value other than the default, then sequences of the whitespace characters space and tab are ignored at the beginning and end of the word, as long as the whitespace character is in the value of IFS (an IFS whitespace character). Any character in IFS that is not IFS whitespace, along with any adjacent IFS whitespace characters, delimits a field. A sequence of IFS whitespace characters is also treated as a delimiter. If the value of IFS is null, no word splitting occurs.

I want to rewrite the following example, so that the delimiter space between the arguments a, b and c is replaced with tab or newline

$ echo a b c
a b c

But when I hit the key Tab, there is no response.

When I hit \ and return, there is no space between a, b and c in the output:

$ echo a\
> b\
> c
abc

Why can't I do what the quote says?

Btw, nothing comes out of $IFS:

$ echo $IFS
  • 2
    echo $IFS isn't going to tell you anything - by definition, you'll have no arguments. You'll want to quote the expansion, and you'll want to see the whitespace characters - try printf '%q\n' "$IFS" instead. – Toby Speight Apr 28 '16 at 16:27
  • echo "$IFS" | od -bc will show it. – waltinator Apr 28 '16 at 16:29
  • You should focus on what you're trying to achieve as I don't think $IFS is meant for what you want. It serves when you want to use non standard delimiters or remove some, which would impact every other commands run in that shell. – Julie Pelletier Apr 28 '16 at 16:46
  • do you mean like echo a$'\n'b$'\n'c and echo a$'\t'b$'\t'c ? – Jeff Schaller Apr 28 '16 at 16:55
  • @JuliePelletier: what is $IFS meant for, if not for what the quote says? – Tim Apr 28 '16 at 17:37

Newlines are handled specially by bash, regardless of the value of IFS. A backslash before a newline causes the newline to be ignored. Has nothing to do with word splitting and would occur even if IFS were set to some custom value.

From LESS=+/^QUOTING man bash:

   A  non-quoted  backslash (\) is the escape character.  It preserves the
   literal value of the next character that follows, with the exception of
   <newline>.   If  a  \<newline>  pair  appears, and the backslash is not
   itself quoted, the \<newline> is treated as a line  continuation  (that
   is, it is removed from the input stream and effectively ignored).

You can see the word splitting on newline occur another way: By stuffing a newline into a variable, and then echoing the variable without quoting it.

$ myvar=a$'\n'b$'\n'c
$ echo "$myvar"
a
b
c
$ echo $myvar
a b c
$ 

Direct answer

The unquoted command: echo a b c, will be split on (metacharacter space) no matter what IFS is or is not. The split arguments to echo: a, b and c will be printed with spaces because echo is defined as this:

LESS=+/'^ *echo \[-neE\] \[arg ...\]' man bash

Output the args, separated by spaces, followed by a newline.

Also no $IFS involved.

So, what you quoted has no relevance here. The key part is:

... splits the results of the other expansions into words ...

You must have an expansion before $IFS is used.
There is not any expansion in a b c.

If you must have an effect of $IFS, use an unquoted expansion:

$ var='a   b   c'
$ echo $var
a b c

But that still does not use $IFS for output, only for splitting.

IFS in output.

Where $IFS does have relevance (for output) is in $*.

$ set -- a b c
$ IFS=$'\t'
$ printf '%s\n' "$*" | od -An -vtx1c
  61  09  62  09  63  0a
   a  \t   b  \t   c  \n

Is that what you need?


Other issues.

Keyboard Tab

when I hit the key Tab, there is no response

Then press together CtrlV, release, and then press Tab.

But without quotes you will not be able to assign a tab to a variable. Test:

$ var=a    b                  ### The space represents a tab as above.
bash: b: command not found

You will need:

$ var="a   b"                 ### The space represents a tab as above.

Double quotes at the very least.

Quote IFS

nothing comes out of $IFS:

$ echo $IFS

Nothing comes out of $IFS because you did not quote $IFS. Try:

<user>$ echo "$IFS" | sed -n l
 \t$
$
<user>$

Which has one new line from $IFS and one from the command echo.
Better change to printf:

<user>$ printf '%s' "$IFS" | sed -n l
 \t$
<user>$

And use od:

$ printf '%s' "$IFS" | od -An -vtx1c
  20  09  0a
      \t  \n

As we already have a way to "see" what characters are contained in an string.
We may as well use that to "see" what is inside a couple of strings:

$ var='a b c'
$ echo "$var" | od -An -vtx1c
  61  20  62  20  63  0a
   a       b       c  \n

That should not be any surprise. As this (with single quotes) should not be:

$ var='a\
> b\
> c'
$ echo "$var" | od -An -vtx1c
  61  5c  0a  62  5c  0a  63  0a
   a   \  \n   b   \  \n   c  \n

Exactly what was typed. If the var is properly quoted, the backslash appear correctly.

Quoting

But quoting with double quotes may change some characters:

$ var="a\
> b\
> c"
$ echo "$var" | od -An -vtx1c
  61  62  63  0a
   a   b   c  \n

But what you wrote has no quotes on setting the var nor using it on echo.
That is a no-no rule in shells. Please Quote correctly.

To replace a space with a tab character using $IFS you can use the argument array:

unset IFS
set a b c
IFS=$(printf \\t)
printf "%s\n" "$*"

Understand that $IFS is about splitting - it contains a list of characters the shell uses to split unquoted expansions in list contexts. You can't really use it to replace characters except in the special case of the $* special shell parameter. In all other cases its use will amount to nullifying and delimiting certain portions of unquoted shell expansions.

  • 1
    Aren't you stuffing arguments in a format string? I mean, you just set them—but otherwise printf "$*\n" would be a pretty bad idea, no? – Wildcard Apr 28 '16 at 19:11
  • @Wildcard - Good point. – mikeserv May 3 '16 at 21:47

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.