Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

unset IFS - unexpected behaviour

Tags:

linux

bash

sh

I thought it was universally agreed that unset IFS restores IFS to its default value.
I cannot find the reason why the following code

echo -n "_${IFS}_" | xxd
IFS=':'
echo -n "_${IFS}_" | xxd
unset IFS
echo -n "_${IFS}_" | xxd
echo "${IFS-IFS is unset}"

returns this

0000000: 5f20 090a 5f                             _ .._
0000000: 5f3a 5f                                  _:_
0000000: 5f5f                                     __
IFS is unset

in both my Ubuntu and Android.
As you can see, IFS is actually unset.
Thanks in advance

like image 222
Claudio Avatar asked Sep 17 '16 10:09

Claudio


3 Answers

I too have seen posts from people saying that if you unset IFS, that restores it to the default. The other answers here help to clarify the story...

On a practical use basis, I would like to add that I did not observe the same thing on Ubuntu and Droid as the op reports. I tested in Ubuntu 18 against /bin/sh, and it worked as expected (like it was restored to the white space characters). But, on Yocto (another Linux distro), using /bin/sh equates to BusyBox (like you'd find on a Droid), unset IFS caused it to act like it were set to NULL, i.e. arguments had no delimiters between them when received in functions etc.

To deal with this, I defined a "constant" DEFAULT_IFS=$' \t\n' and assigned IFS to that rather than using unset. That method worked across contexts for me.

like image 130
BuvinJ Avatar answered Nov 24 '22 01:11

BuvinJ


I thought it was universally agreed that unset IFS restores IFS to its default value.

This is plain wrong and there are no mentions of this in any documentations!


Let's search for IFS in the reference manual and see what we can learn:

3.4.2 Special Parameters

* ($*) Expands to the positional parameters, starting from one. When the expansion is not within double quotes, each positional parameter expands to a separate word. In contexts where it is performed, those words are subject to further word splitting and pathname expansion. 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. That is, $* is equivalent to $1c$2c…, where c is the first character of the value of the IFS variable. If IFS is unset, the parameters are separated by spaces. If IFS is null, the parameters are joined without intervening separators.

Try it:

$ set -- one two three
$ IFS=hello
$ echo "$*"
onehtwohthree
$ unset IFS
$ echo "$*"
one two three
$

Similar expansions will occur with the array-like expansions: "${array[*]}" and "${!prefix*}".

3.5.7 Word Splitting

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.

Maybe the confusion comes from the part in bold.

The reference for the read builtin refers to this section too, so we won't learn anything new there. The other mentions of IFS will not bring anything new to the picture.


Conclusion.

No, unsetting IFS will not reset it to its default value. There are no mentions of that anywhere in the manual. The confusion comes from the fact that the manual specifies that, for word splitting (and the * form of array-like parameters), an unset IFS yields the same behavior as the default value of IFS.

like image 43
gniourf_gniourf Avatar answered Nov 24 '22 00:11

gniourf_gniourf


Quoting POSIX / http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05:

If the value of IFS is a <space>, <tab>, and <newline>, or if it is unset, ...

This does not mean unsetting IFS automatically resets it to " \t\n". It simply means if you unset IFS, word splitting is done as if IFS were set to " \t\n".

like image 34
melpomene Avatar answered Nov 24 '22 01:11

melpomene