使用非空格字符设置IFS时,在Bash中进行单词分割

8
我正在学习一个关于Bash的教程, 主要学习单词分割的内容。
这个叫做"args"的脚本,有助于演示单词分割的例子:
#!/usr/bin/env bash
printf "%d args:" $#
printf " <%s>" "$@"
echo

一个例子:
$ ./args hello world, here is "a string of text!"
5 args: <hello> <world,> <here> <is> <a string of text!>

到目前为止还不错,我明白这是如何工作的。
然而,当我使用非空格字符(比如:)替换IFS时,如果我直接将字符串作为参数传递,脚本就不会执行单词拆分。
$ ./args one:two:three
1 args: <one:two:three>

然而,如果我将字符串(1)赋值给变量,然后(2)通过参数扩展将字符串传递给脚本,脚本确实对同一字符串执行单词拆分。

$ IFS=:
$ variable="one:two:three"
$ ./args $variable
3 args: <one> <two> <three>

为什么当IFS未设置且分隔符是空格字符时,将字符串作为参数传递时会进行单词拆分,但是当IFS设置为非空格字符时不会进行单词拆分?为什么使用read而不是此脚本时,同样的字符串也会按预期进行单词拆分。
$ IFS=:
$ read a b c
one:two:three
$ echo $a $b $c
one two three
1个回答

11

关于单词拆分,您可以在这里阅读更多内容。

对于未出现在双引号内的参数展开、命令替换和算术展开的结果,shell会扫描并进行单词拆分。

如果您将裸字符串one:two:three作为IFS设置为:的参数传递,Bash不会进行单词拆分,因为裸字符串不属于参数展开、命令替换或算术展开上下文之一。

然而,当相同的字符串被赋值给一个变量,并且该变量未被引用地传递到脚本中时,单词拆分会发生,因为这是参数展开的情况。

对于这些情况(命令替换),也是一样的。

$ ./args $(echo one:two:three)
3 args: <one> <two> <three>

$ ./args "$(echo one:two:three)"
1 args: <one:two:three>

文档所述,read命令在读取每行时会进行单词拆分,除非已将IFS设置为空字符串。



1
干得好,你把它搞定了 :) - David C. Rankin
我知道参数扩展将会按照gnu.org中详细说明的shell扩展顺序规则后跟单词拆分。此外,在您发送给我的第一个链接中,最后一行写道:“请注意,如果没有发生扩展,则不执行拆分。”明白了。那么,当我执行./args hello world, here is "a string of text!"时,为什么这个脚本中仍然会触发单词拆分呢?我在那里没有明确执行参数扩展,但是进行了单词拆分,这意味着某些东西触发了扩展。 - Nadim Hussami
1
我唯一看到的是,当IFS设置为默认的空格字符时,裸字符串会触发单词拆分,但当IFS设置为非空格字符时,除非通过参数扩展、命令替换或类似扩展显式调用,否则不会触发。我猜这更多地与printf内置有关,以及我对它的工作方式缺乏理解。当你说“按文档记录,read命令确实会在读取每行时进行单词拆分,除非IFS已设置为空字符串”时,你基本上已经这么说了。似乎printf做事情的方式不同。 - Nadim Hussami
2
常规参数被处理为由空格分隔的令牌列表,因此在printf中没有IFS。 - codeforester
1
太好了,谢谢!这最后一条评论是我想问的问题所缺失的信息。 - Nadim Hussami

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