How do I correctly run a few commands with an altered value of the IFS
variable (to change the way field splitting works and how "$*"
is handled), and then restore the original value of IFS
?
I know I can do
(
IFS='my value here'
my-commands here
)
to localize the change of IFS
to the sub-shell, but I don't really want to start a sub-shell, especially not if I need to change or set the values of variables that needs to be visible outside of the sub-shell.
I know I can use
saved_IFS=$IFS; IFS='my value here'
my-commands here
IFS=$saved_IFS
but that seems to not restore IFS
correctly in the case that the original IFS
was actually unset.
Looking for answers that are shell agnostic (but POSIX).
Clarification: That last line above means that I'm not interested in a bash
-exclusive solution. In fact, the system I'm using most, OpenBSD, does not even come with bash
installed at all by default, and bash
is not a shell I use for anything much other than to answer questions on this site. It's much more interesting to see solutions that I may use in bash
or other POSIX-like shells without making an effort to write non-portable code.
Best Answer
Yes, in the case when
IFS
is unset, restoring the value from$saved_IFS
would actually set the value ofIFS
(to an empty value).This would affect the way field splitting of unquoted expansions is done, it would affect field splitting for the
read
built-in utility, and it would affect the way the positional parameters are combined into a string when using"$*"
.With an unset
IFS
these things would happen as ifIFS
had the value of a space, a tab character, and a newline character, but with an empty value, there would be no field splitting and the positional parameters would be concatenated into a string with no delimiter when using"$*"
. So, there's a difference.To correctly restore
IFS
, consider settingsaved_IFS
only ifIFS
is actually set to something.The parameter substitution
${IFS+set}
expands to the stringset
only ifIFS
is set, even if it is set to an empty string. IfIFS
is unset, it expands to an empty string, which means that the-n
test would be false andsaved_IFS
would remain unset.Now,
saved_IFS
is unset ifIFS
was initially unset, or it has the value thatIFS
had, and you can set whatever value you want forIFS
and run your code.When restoring
IFS
, you do a similar thing:The final
unset saved_IFS
isn't really necessary, but it may be good to clean up old variables from the environment.An alternative way of doing this, suggested by LL3 in comments (now deleted), relies on prefixing the
unset
command by:
, a built-in utility that does nothing, effectively commenting out theunset
, when it's not needed:This sets
saved_IFS
to the value of$IFS
, but then unsets it ifIFS
was unset.Then set
IFS
to your value and run you commands. Then restore with(possibly followed by
unset saved_IFS
if you want to clean up that variable too).Note that
:
must be quoted, as above, or escaped as\:
, so that it isn't modified by$IFS
containing:
(the unquoted parameter substitution invokes field splitting, after all).