Bash单词分割机制

3

我是Bash的新手,发现Bash会自动进行单词拆分:

a="1  2     3 4"

如果我用echo $a输出"a",那么我会得到1 2 3 4,这是因为隐式地进行了单词分割。如果我循环使用"a",则分别得到1、2、3和4。
我还从这里了解到:

Shell会扫描未在双引号内出现的参数扩展、命令替换和算术扩展的结果以进行单词分割。

我还发现,如果我有以下内容:
b=$a;
echo "$b"

I would get

"1 2 3 4"

那么,这是我的问题:什么时候进行单词拆分?它会改变字符串本身吗?它只有在我使用 echofor(循环)时才生效吗?

更一般地说,bash 如何处理它?


有用的参考资料:GNU文档Greg's Wiki - Word Splitting - codeforester
3个回答

7
实际上有几轮单词分割。第一轮是在解析命令行之前执行的,因此echo $a被分成两个单词echo$a。(这就是为什么a="echo foo | wc -l"; $a这样的语句不执行管道操作的原因;在$a扩展之前解析已经完成)。在单词分割结束后,参数扩展会生成2个字符串,echo1 2 3 4。由参数扩展产生的字符串本身也会进行单词分割,因为它没有被引用,从而产生了4个额外的单词1234
在for循环中,列表中的项目会被进行单词分割:
for b in $a; do

经过单词分割后,for, b, in, $a, ;, 和 do 被扩展为 for, b, in, 1 2 3 4, ;, 和 do。再次,由参数扩展产生的字符串被进行单词分割,变成了 1, 2, 3, 和 4


4
当解析脚本或命令行时,Bash的工作方式如下:
  1. 解析和词法分析
  2. 展开
    1. 花括号扩展
    2. 波浪线扩展
    3. 变量扩展
    4. 算术和其他替换
    5. 命令替换
    6. 单词拆分
    7. 文件名生成(globbing)
  3. 去除引号
正如您所看到的,单词拆分几乎在最后,例如在算术扩展之后但在文件名生成之前,去除引号是最后一步。

1
对于不太熟悉 bash 的读者:虽然引号的移除是最后一步,但它仅会移除_扩展之前已经存在的引号_。如果需要一些元编程,我们需要使用 sh -c "带有$变量的命令"eval "带有$变量的命令" - Yuning

2

请阅读 man bash。对于赋值操作,它表示:

所有的值都会经过波浪线扩展、参数和变量扩展、命令替换、算术扩展和引号去除[...]。不执行单词分割,除了在特殊参数下解释的"$@"。路径名扩展也不会进行。

[[ ]]条件中,也不会发生单词分割:

在 [[ 和 ]] 之间的单词不进行单词分割和路径名扩展。


非常感谢!但是bash在调用时是否进行了单词分割而不仅仅是一次?我的意思是,Bash总是在认为必要时执行拆分,而不仅仅是第一次执行并保存它吗?如果它对相同的字符串进行多次拆分,那么效率就没有考虑得很好? - dragonxlwang
如果你需要效率,不要使用bash。 - choroba

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