我假设设置IFS='X'
会导致bash
将字符串拆分fooXbarXbaz
为三个单词:foo
、bar
和baz
。
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
(即使在那里,关键字和变量名也没有在o
s 上分开。)
虽然使用o
or X
inIFS
有点极端,但现在最好的情况是分裂只发生在扩展。
如果您有意使用拆分,请记住,未加引号的扩展也会进行通配符处理,因此类似的事情*
可能会导致意外。数组通常更适合处理多个值。
答案3
GNU bash 文档清楚地解释了这种行为命令替换
如果替换出现在双引号内,则不会对结果执行分词和文件名扩展。
由于第二种情况有一个不带引号的替换,因此fooXbarXbaz
shell 根据 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 进行不可预见的分词。