Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to set IFS for a for loop and then unset it inside the for loop?

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 unsetting 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
like image 695
Susam Pal Avatar asked Mar 04 '15 15:03

Susam Pal


People also ask

What is IFS in while loop?

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.

What is the default IFS value?

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.


1 Answers

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
like image 132
glenn jackman Avatar answered Oct 14 '22 09:10

glenn jackman