IFS=: 在循环遍历冒号分隔值时会导致不同的行为。

12

实验 1

这是我在名为foo.sh的文件中的第一个脚本。

IFS=:
for i in foo:bar:baz
do
    echo $i
done

这会产生以下输出。

$ bash foo.sh
foo bar baz

实验2

这是我的第二个脚本。

IFS=:
for i in foo:bar:baz
do
    unset IFS
    echo $i
done
这将产生以下输出。

这将产生以下输出。

$ bash foo.sh
foo:bar:baz

实验三

这是我的第三个脚本。

IFS=:
var=foo:bar:baz
for i in $var
do
    echo $i
done

这将产生以下输出。

$ bash foo.sh
foo
bar
baz
为什么三种情况下的输出结果不同?你能否解释IFS的解释规则以及导致这些不同输出的命令吗?

为什么三种情况下的输出结果不同?你能否解释IFS的解释规则以及导致这些不同输出的命令吗?


2
你混淆了两件不同的事情:for i in foo:bar:baz 的行为和 echo $i 中未引用扩展的行为。如果你使用 echo "$i" 进行测试,那么这将让你仅隔离出 for i in foo:bar:baz 的行为(以及它如何完全不受 IFS 影响),这可以说是你问题的核心 - 相比之下,一个仅询问 echo $i 行为而没有周围的 for 的问题将是一个简单的重复,可以通过指向已经存在的问题和答案来回答。 - Charles Duffy
1个回答

16

我觉得这是一个非常有趣的实验。 谢谢你。


要理解正在发生的事情, 来自man bash的相关部分如下:

  字符串拆分
      在未出现在双引号内的参数扩展、命令替换和算术扩展结果上对其进行字符串拆分。

关键在于"results of ..."部分,它非常微妙。 也就是说,字符串拆分发生在特定操作的结果上, 如参数扩展的结果、命令替换的结果等。 字符串拆分不会对字符串文字(例如foo:bar:baz)执行。

让我们看看这个逻辑在您的示例中如何运作。

实验1

IFS=:
for i in foo:bar:baz
do
    echo $i
done

这将产生以下输出:

foo bar baz

对于字面值foo:bar:baz,没有执行单词拆分, 因此,只要涉及for循环,IFS的值是什么都无所谓。

在参数扩展后,会对$i的值执行单词拆分, 因此,foo:bar:baz被拆分为3个单词,并传递给echo,因此输出是foo bar baz

实验2

IFS=:
for i in foo:bar:baz
do
    unset IFS
    echo $i
done

这将产生以下输出:

foo:bar:baz

再次说明, 对于字面值foo:bar:baz,不会执行任何单词拆分, 因此无论IFS的值是什么, 就for循环而言都没有关系。

在参数扩展后,将执行单词拆分 $i 的值, 但由于IFS未设置, 它使用默认值来执行拆分, 即<space><tab><newline>。 但由于foo:bar:baz不包含任何这些字符, 因此保持不变,输出结果为foo:bar:baz

实验三

IFS=:
var=foo:bar:baz
for i in $var
do
    echo $i
done

这将产生以下输出:

foo
bar
baz

$var 参数扩展之后,使用 IFS 的值执行单词分割,因此 for 有三个值要迭代,即 foobarbaz。 这里的 echo 行为很简单,输出每行一个单词。


底线是:字面值不会执行单词分割。 单词拆分仅在特定操作的结果上执行。

这并不奇怪。 字符串字面值很像用双引号括起来的表达式,您不会期望在 "..." 上进行单词拆分。


2
很棒的答案,这应该会得到悬赏!这里也有类似的问题/答案:使用printf和将IFS设置为非空格字符的Bash中的单词拆分 - codeforester

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