取消IFS - 意外行为

11

我认为广泛认同的是,unset IFS 会将 IFS 环境变量恢复为其默认值。
我找不到以下代码出错的原因。

echo -n "_${IFS}_" | xxd
IFS=':'
echo -n "_${IFS}_" | xxd
unset IFS
echo -n "_${IFS}_" | xxd
echo "${IFS-IFS is unset}"

返回这个

0000000: 5f20 090a 5f                             _ .._
0000000: 5f3a 5f                                  _:_
0000000: 5f5f                                     __
IFS is unset

在我的Ubuntu和Android系统中都是如此。
正如您所看到的,IFS实际上是未设置的。
提前致谢。


1
明显的跨站点重复 http://unix.stackexchange.com/questions/97481/ifs-null-is-not-the-same-as-unset-ifs - tripleee
@anubhava 这很出乎意料,IFS 没有恢复到默认值 $' \t\n',就像 @tripleee 的链接所解释的那样。你们真的读懂我的问题了吗? - Claudio
unset IFS 确实会取消设置 IFS。它不会被设置为 $' \t\n'。虽然空的 IFS 也像将其设置为 $' \t\n' 一样运作。 - anubhava
谢谢@anubhava,现在我明白了。 - Claudio
@anubhava 这只在某些情况下是正确的。如果您unset IFS,则在其他情况下可能会导致难以找到的错误。 - Stefan Fabian
3个回答

15

我曾认为通常认为未设置IFS会将IFS恢复为其默认值。

这是完全错误的,没有任何文件中提到这一点!


让我们在参考手册中搜索IFS并了解一些信息:

3.4.2 特殊参数

* ($*) 扩展为位置参数,从一个开始。当扩展不在双引号内时,每个位置参数扩展为一个单独的单词。在它被执行的上下文中,那些单词还会被进一步的单词拆分和路径名扩展。当扩展出现在双引号内时,它会扩展为一个单词,其中每个参数的值由IFS特殊变量的第一个字符分隔。也就是说,$*相当于$1c$2c…,其中是变量IFS值的第一个字符。 如果未设置IFS,则参数将由空格分隔。如果IFS为空,则参数将无间隔符加入一起。

试一下吧:

$ set -- one two three
$ IFS=hello
$ echo "$*"
onehtwohthree
$ unset IFS
$ echo "$*"
one two three
$

类似的扩展还会出现在数组式的扩展中:"${array[*]}""${!prefix*}"

3.5.7 单词分割

Shell将$IFS的每个字符视为定界符,使用这些字符作为字段分隔符将其他扩展的结果拆分成单词。如果IFS未设置或其值正好为默认值(即<space><tab><newline>),则忽略先前扩展结果的开头和结尾处的<space><tab><newline>序列,任何IFS字符序列不位于开头或结尾的位置都用于分隔单词。

也许混淆是来自粗体部分。

read内置命令的参考文献也引用了这一节,因此我们在那里不会学到任何新东西。关于IFS的其他提及不会为此带来任何新内容。


结论。

不,取消设置IFS不会将其重置为默认值。手册中没有任何提到这一点的内容。混淆来自于手册指定,对于单词分割(以及类似数组参数的*形式),未设置IFS会产生与IFS默认值相同的行为。


7

我也看到有人说如果您取消设置IFS,那么它将恢复为默认值。这里的其他答案有助于澄清事情的来龙去脉...

从实际应用的角度出发,我想补充一点,就是在Ubuntu和Droid上,我并没有像op报告的那样观察到相同的情况。我在Ubuntu 18中针对/bin/sh进行了测试,结果如预期(就像它被“恢复”为空格字符)。但是,在Yocto(另一个Linux发行版)中使用/bin/sh等同于BusyBox(就像您在Droid上找到的那样),unset IFS会使其表现得像设置为NULL,即在函数等接收参数时没有分隔符。

为了解决这个问题,我定义了一个“常量”DEFAULT_IFS=$' \t\n',并将IFS分配给它,而不是使用unset。这种方法在我的所有上下文中都有效。


5

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接