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
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.
I thought it was universally agreed that unset
IFS
restoresIFS
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:
*
($*
) 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 theIFS
special variable. That is,$*
is equivalent to$1c$2c…
, wherec
is the first character of the value of theIFS variable
. IfIFS
is unset, the parameters are separated by spaces. IfIFS
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*}"
.
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. IfIFS
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 ofIFS
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.
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
.
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"
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With