什么是分词?为什么它在 shell 编程中很重要?

Ame*_*ina 21 shell zsh

我对分词在zsh. 我在用 C、Python 或 MATLAB 编程时没有接触过这个概念,这引发了我的兴趣,为什么分词似乎是特定于 shell 编程的东西。

我之前在这个网站和其他网站上读过关于分词的文章,但没有找到对这个概念的明确解释。维基百科有分定义,但似乎没有关于它如何应用于 Unix shell 的参考。

这是我在以下方面的困惑的一个例子zsh

Z Shell FAQ 中,我阅读了以下内容:

3.1: 为什么$varwherevar="foo bar"没有按照我的预期做?

在大多数 Bourne-shell 衍生产品中,多词变量(例如) var="foo bar" 在传递给命令或在for foo in $var循环中使用时被拆分为词。默认情况下, zsh 没有这种行为:变量保持不变。(这不是错误!见下文。)该选项的SH_WORD_SPLIT存在是为了提供兼容性。

但是,在 Z Shell 手册中,我阅读了以下内容:

SH_WORD_SPLIT (-y) <K> <S>

导致对不带引号的参数扩展执行字段拆分。请注意,此选项 与分词无关。(参见参数扩展。)

为什么会说SH_WORD_SPLIT没有做与分词?分词不正是这一切的意义所在吗?

Gil*_*il' 27

早期的 shell 只有一种数据类型:字符串。但是操作字符串列表很常见,通常是在将多个文件名作为参数传递给程序时。拆分的另一个常见用例是当命令输出结果列表时:命令的输出是一个字符串,但所需的数据是一个字符串列表。要将文件名列表存储在变量中,您需要在它们之间放置空格。然后像这样的shell脚本

files="foo bar qux"
myprogram $files
Run Code Online (Sandbox Code Playgroud)

所谓myprogram三个参数,作为外壳分割字符串$files成单词。当时,文件名中的空格要么被禁止,要么被广泛认为未完成。

Korn shell中引入了阵列:你可以字符串列表存储在一个变量。Korn shell 仍然与当时建立的 Bourne shell 兼容,因此裸变量扩展不断进行分词,并且使用数组需要一些语法开销。你会写上面的片段

files=(foo bar qux)
myprogram "${files[@]}"
Run Code Online (Sandbox Code Playgroud)

Zsh 从一开始就有数组,它的作者以牺牲向后兼容性为代价选择了更明智的语言设计。在zsh中(默认扩展规则下)$var不进行分词;如果你想在一个变量中存储一个单词列表,你应该使用一个数组;如果你真的想要分词,你可以写$=var.

files=(foo bar qux)
myprogram $files
Run Code Online (Sandbox Code Playgroud)

如今,文件名中的空格是您需要处理的问题,这既是因为许多用户希望它们能够正常工作,也因为许多脚本是在攻击者可能控制文件名的安全敏感环境中执行的。所以自动分词通常很麻烦;因此,我的一般建议是始终使用双引号,即 write "$foo",除非您了解为什么在特定用例中需要分词。(请注意,裸变量扩展也会进行通配。)

  • @intrpc“分词”不是在自然语言单词上拆分,而是在`$IFS`字符上拆分。因此,“字段拆分”是一个更好的名称。但是在 shell 文献中,这个概念经常使用“分词”。zsh 文档在措辞上争论不休。 (2认同)

小智 5

在 Zsh 的这种特定情况下,单词拆分的定义与字段拆分略有不同。

考虑一下prog a b c,无论您如何设置,它都会传入三个参数IFS。这是分

如果这样做A="a b c"; prog $A,如果IFS包含空格,它将传入三个参数,否则将传入一个参数。这是分裂。

这里的定义很微妙。Zsh 文档试图说明的是,即使您禁用该选项,prog a b c仍将获得单独的参数(这是人们一直期望的)。

  • 长期从事 zsh 开发的 Bart Schaefer [确认这确实是该文本的预期含义](https://www.zsh.org/mla/workers/2018/msg00253.html)。 (3认同)