bash 取消设置 IFS - 意外行为

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39545837/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 15:11:55  来源:igfitidea点击:

unset IFS - unexpected behaviour

linuxbashsh

提问by Claudio

I thought it was universally agreed that unset IFSrestores IFS to its default value.
I cannot find the reason why the following code

我认为unset IFS将 IFS 恢复为其默认值已成为普遍共识。
我找不到以下代码的原因

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

returns this

返回这个

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

in both my Ubuntu and Android.
As you can see, IFS is actually unset.
Thanks in advance

在我的 Ubuntu 和 Android 中。
如您所见,IFS 实际上是未设置的。
提前致谢

回答by gniourf_gniourf

I thought it was universally agreed that unset IFSrestores IFSto its default value.

我认为普遍同意 unsetIFS恢复IFS到其默认值。

This is plain wrong and there are no mentions of this in any documentations!

这是完全错误的,并且在任何文档中都没有提到这一点!



Let's search for IFSin the reference manualand see what we can learn:

让我们IFS参考手册中搜索,看看我们可以学到什么:

3.4.2 Special Parameters

3.4.2 特殊参数

*($*) Expands to the positional parameters, starting from one. When the expansion is not within double quotes, each positional parameter expands to a separate word. In contexts where it is performed, those words are subject to further word splitting and pathname expansion. When the expansion occurs within double quotes, it expands to a single word with the value of each parameter separated by the first character of the IFSspecial variable. That is, $*is equivalent to $1c$2c…, where cis the first character of the value of the IFS variable. If IFSis unset, the parameters are separated by spaces.If IFSis null, the parameters are joined without intervening separators.

*( $*) 扩展到位置参数,从 1 开始。当扩展不在双引号内时,每个位置参数扩展为一个单独的词。在执行它的上下文中,这些词会受到进一步的分词和路径名扩展。当扩展发生在双引号内时,它扩展为单个单词,每个参数的值由IFS特殊变量的第一个字符分隔。也就是说,$*相当于$1c$2c…,其中c是 的值的第一个字符IFS variable如果IFS未设置,则参数以空格分隔。如果IFS为 null,则连接参数而不插入分隔符。

Try it:

尝试一下:

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

Similar expansions will occur with the array-like expansions: "${array[*]}"and "${!prefix*}".

类似的扩展将发生在类似数组的扩展中:"${array[*]}""${!prefix*}"

3.5.7 Word Splitting

3.5.7 分词

The shell treats each character of $IFSas a delimiter, and splits the results of the other expansions into words using these characters as field terminators. If IFSis unset, or its value is exactly <space><tab><newline>, the default,then sequences of <space>, <tab>, and <newline>at the beginning and end of the results of the previous expansions are ignored, and any sequence of IFScharacters not at the beginning or end serves to delimit words.

Shell 将 的每个字符$IFS视为分隔符,并使用这些字符作为字段终止符将其他扩展的结果拆分为单词。如果IFS没有设置,或者它的值是完全<space><tab><newline>,默认值,然后序列<space><tab>以及<newline>在开始和以前扩张的结果最终会被忽略,而任何序列IFS在开始时没有字符或结束用于分隔单词。

Maybe the confusion comes from the part in bold.

也许混乱来自粗体部分。

The reference for the readbuiltin refers to this section too, so we won't learn anything new there. The other mentions of IFSwill not bring anything new to the picture.

read内置函数的参考也参考了这一节,所以我们不会在那里学到任何新东西。其他提到的IFS不会给图片带来任何新的东西。



Conclusion.

结论。

No, unsetting IFSwill notreset it to its default value. There are no mentions of that anywhere in the manual. The confusion comes from the fact that the manual specifies that, for word splitting (and the *form of array-like parameters), an unset IFSyields the same behavior as the default value of IFS.

不,解封IFS其重置为默认值。手册中的任何地方都没有提及。混淆来自这样一个事实,即手册指定,对于分词(以及*类似数组的参数的形式),未设置会IFS产生与 的默认值相同的行为IFS

回答by melpomene

Quoting POSIX / http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05:

引用 POSIX / http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05

If the value of IFSis a <space>, <tab>, and <newline>, or if it is unset, ...

如果IFS 的值为 <space>、<tab> 和 <newline>,或者未设置,...

This does not mean unsetting IFSautomatically resets it to " \t\n". It simply means if you unset IFS, word splitting is done as if IFSwere set to " \t\n".

这并不意味着取消设置会IFS自动将其重置为" \t\n"。它只是意味着如果您取消设置IFS,则分词就像IFS设置为 一样完成" \t\n"

回答by BuvinJ

I too have seen posts from people saying that if you unset IFS, that restores it to the default. The other answers here help to clarify the story...

我也看到有人发帖说,如果您取消设置 IFS,就会将其恢复为默认值。这里的其他答案有助于澄清这个故事......

On a practical use basis, I would like to add that I did notobserve the same thing on Ubuntu and Droid as the op reports. I tested in Ubuntu 18 against /bin/sh, and it worked as expected (like it was restoredto the white space characters). But, on Yocto (another Linux distro), using /bin/shequates to BusyBox (like you'd find on a Droid), unset IFScaused it to act like it were set to NULL, i.e. arguments had no delimiters between them when received in functions etc.

在实际使用的基础上,我想补充一点,我在 Ubuntu 和 Droid 上没有观察到与 op 报告的相同的事情。我在 Ubuntu 18 中针对 进行了测试/bin/sh,它按预期工作(就像恢复为空白字符一样)。但是,在 Yocto(另一个 Linux 发行版)上,使用/bin/sh等同于 BusyBox(就像您在 Droid 上发现的那样),unset IFS导致它的行为就像设置为NULL,即在函数等中接收参数时,它们之间没有分隔符。

To deal with this, I defined a "constant" DEFAULT_IFS=$' \t\n'and assigned IFS to that rather than using unset. That method worked across contexts for me.

为了解决这个问题,我定义了一个“常量”DEFAULT_IFS=$' \t\n'并将 IFS 分配给它,而不是使用unset. 这种方法对我来说适用于各种情况。