为什么不设置 IFS shell 变量在 for 循环中将字符串拆分为单词,除非使用 $(echo ...) ?

为什么不设置 IFS shell 变量在 for 循环中将字符串拆分为单词,除非使用 $(echo ...) ?

我假设设置IFS='X'会导致bash将字符串拆分fooXbarXbaz为三个单词:foobarbaz

for但是,只有通过命令替换将字符串提供给循环时它才有效$(echo fooXbarXbaz)::

$ IFS='X'; for x in fooXbarXbaz; do echo Y${x}Z; done
Yfoo bar bazZ
$ IFS='X'; for x in $(echo fooXbarXbaz); do echo Y${x}Z; done
YfooZ
YbarZ
YbazZ

有人可以解释为什么第一个示例命令无法分割fooXbarXbaz成三个单词,而第二个示例却成功了?

答案1

$IFS仅用于未加引号的扩展后的分词。中没有扩展for x in fooXbarXbaz。然而, 中有一个未加引号的扩展echo Y${x}Z,这意味着echo使用三个参数Yfoo,bar和 来调用它bazZ。该echo实用程序打印其每个参数,中间有一个空格,因此您得到Yfoo bar bazZ.

在第二个示例中,字符串是命令替换的输出,这是一个扩展,因此字符串被拆分以进行循环。

IFS变量还用于分割read实用程序的输入。

答案2

有人可以解释为什么第一个示例命令无法分成fooXbarXbaz三个单词

因为 shell 不应该这样工作再多一点

在某些古代,简单单词也发生分词,例如 withheirloom-sh

heirloom-sh$ for foo in 123o456o789; do echo $foo; done
ech: not found
ech: not found
ech: not found

哎呀,我的意思是:

heirloom-sh$ for foo in 123o456o789; do "echo" $foo; done
123
456
789

(即使在那里,关键字和变量名也没有在os 上分开。)

虽然使用oor XinIFS有点极端,但现在最好的情况是分裂只发生在扩展

如果您有意使用拆分,请记住,未加引号的扩展也会进行通配符处理,因此类似的事情*可能会导致意外。数组通常更适合处理多个值。

答案3

GNU bash 文档清楚地解释了这种行为命令替换

如果替换出现在双引号内,则不会对结果执行分词和文件名扩展。

由于第二种情况有一个不带引号的替换,因此fooXbarXbazshell 根据 set 的值对生成的字符串进行分词IFS,从而将单词拆分为多个单词。

在第一种情况下for x in fooXbarXbaz;,字符串会进行路径名扩展(也称为全局扩展)。 shell 在完成所有可能的扩展之后,在比分词晚得多的阶段执行此操作(请参阅外壳扩展),因此结果字符串按字面意思处理。

要进一步添加变量扩展的情况$var,它发生在路径名扩展之前,经历与您的OP相同的过程

IFS='X'; var=fooXbarXbaz; for x in $var; do echo Y${x}Z; done

不带引号的变量扩展(例如命令替换)会根据 的值进行单词分割IFS。这就是为什么我们通常坚持引用所有扩展以避免 shell 进行不可预见的分词。

相关内容