Consider the following shell script that I intend to run on any POSIX.1-2004 conformant shell.
log()
{
echo debug: "$*"
}
A=foo:bar:baz
IFS=:
for i in $A
do
log token ">> >>" "$i"
done
unset IFS
# Do more stuff here
I want to loop over colon separated values. Within the loop I want to call a log function that should be able to echo all arguments passed to it intact with multiple whitespaces in each argument preserved. When I run this code, this is the output I get.
debug: token:>> >>:foo
debug: token:>> >>:bar
debug: token:>> >>:baz
The good thing is that the two spaces in ">> >>"
have been preserved because of the usage of "$*"
, i.e. quoted special parameter asterisk as defined in Section 2.5.2 Special Parameters of POSIX.1-2004.
But the bad thing is that as a result of the same usage, the three arguments passed to log
are now separated by colon (the IFS
).
I solve this problem by unset
ting the IFS
within the for
loop.
log()
{
echo debug: "$*"
}
A=foo:bar:baz
IFS=:
for i in $A
do
unset IFS
log token ">> >>" "$i"
done
unset IFS
# Do more stuff here
Now, the output is as I desire.
debug: token >> >> foo
debug: token >> >> bar
debug: token >> >> baz
But I want to know if it is possible that in some POSIX shell, unsetting IFS
within the for
loop can have the side-effect of modifying the IFS
for the for
loop while it is in progress?
For example, I worry that some POSIX shell might produce the following output for my second code example.
debug: token >> >> foo
debug: token >> >> bar:baz
Can someone tell me by citing the relevant sections from POSIX.1-2004 if my second code example is safe, and if it is guaranteed to produce the same behavior in all POSIX compliant shells?
If my second code example is not safe, then I might have to settle for something like this that does not modify the IFS
.
log()
{
echo debug: "$*"
}
A=foo:bar:baz
echo "$A" | tr : "\n" | while read i
do
log token ">> >>" "$i"
done
IFS is used to set field separator (default is while space). The -r option to read command disables backslash escaping (e.g., \n, \t). This is failsafe while read loop for reading text files.
The default value of IFS is space, tab, newline. (A three-character string.) If IFS is unset, it acts as though it were set to this default value.
It is safe.
The $A
variable will be expanded once before the for-loop starts executing:
for i in foo bar baz
Once that happens, any changes to IFS will have no effect.
Refs:
http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06
http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_05
If you are using IFS just to parse that string, you could do this:
oldIFS=$IFS IFS=:
set -- $A # save the elements of the list as positional parameters
IFS=$oldIFS
for i; do
log "$i"
done
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