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
IFSrestoresIFSto 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 theIFSspecial variable. That is,$*is equivalent to$1c$2c…, wherecis the first character of the value of theIFS variable. IfIFSis unset, the parameters are separated by spaces. IfIFSis 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
$IFSas a delimiter, and splits the results of the other expansions into words using these characters as field terminators. IfIFSis 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 ofIFScharacters 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