为for循环设置IFS然后在for循环中取消设置是否安全?

时间:2015-03-04 15:57:31

标签: linux shell unix posix

考虑我打算在任何符合POSIX.1-2004的shell上运行的以下shell脚本。

log()
{
    echo debug: "$*"
}

A=foo:bar:baz

IFS=:
for i in $A
do
   log token ">>  >>" "$i"
done

unset IFS
# Do more stuff here

我想循环冒号分隔的值。在循环中,我想调用一个日志函数,它应该能够回显传递给它的所有参数,并保留每个参数中的多个空格。当我运行此代码时,这是我得到的输出。

debug: token:>>  >>:foo
debug: token:>>  >>:bar
debug: token:>>  >>:baz

好处是">> >>"中的两个空格由于"$*"的使用而被保留,即在POSIX.1-2004的Section 2.5.2 Special Parameters中定义的引用特殊参数星号

但不好的是,由于相同的用法,传递给log的三个参数现在用冒号(IFS)分隔。

我通过unset IFS循环内的for解决了这个问题。

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

现在,输出正如我所愿。

debug: token >>  >> foo
debug: token >>  >> bar
debug: token >>  >> baz

但是我想知道在某些POSIX shell中是否有可能在IFS循环中取消设置for会产生为IFS修改for的副作用1}}循环正在进行中?

例如,我担心某些POSIX shell可能会为我的第二个代码示例生成以下输出。

debug: token >>  >> foo
debug: token >>  >> bar:baz

有人可以通过引用POSIX.1-2004中的相关部分来告诉我,如果我的第二个代码示例是安全的,并且是否保证在所有符合POSIX的shell中产生相同的行为?

如果我的第二个代码示例不安全,那么我可能不得不接受这样的不会修改IFS的内容。

log()
{
    echo debug: "$*"
}

A=foo:bar:baz

echo "$A" | tr : "\n" | while read i
do
   log token ">>  >>" "$i"
done

1 个答案:

答案 0 :(得分:6)

这是安全的。

在for循环开始执行之前,$A变量将被展开一次:

for i in foo bar baz

一旦发生这种情况,对IFS的任何更改都将无效。

参考文献:
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

如果您正在使用IFS来解析该字符串,您可以这样做:

oldIFS=$IFS IFS=:
set -- $A           # save the elements of the list as positional parameters
IFS=$oldIFS

for i; do
    log "$i"
done