我正在尝试验证我在这里读到的以下内容:
重要的是要记住,shell 执行我们按顺序描述的所有类型的扩展。这意味着单词扩展在路径名扩展之前执行。因此,如果您循环遍历扩展路径的结果,则不会对这些结果执行分词。
我编写了一个非常简单的脚本,用于验证上面关于单词和路径名扩展的操作顺序的引用:
$ cat test.sh
for path in /home/john/Downloads/*.xlsx
do
echo "${path}"
done
结果
$ ./test.sh
/home/john/Downloads/CCIE-Collaboration-v3-Learning-Matrix.xlsx
/home/john/Downloads/x y.xlsx
据我所知,单词扩展发生在路径扩展之前,他们使用了一个与此非常相似的脚本来演示它。但我实在不明白这个例子是如何演示的?据我所知,“path”是一个变量,在该变量标识路径之后,该变量将是$path。那么地球上哪里会有单词扩展呢,因为我看不到任何可以考虑扩展的单词。
为了澄清,如果我们运行第一次迭代,$path 是空的,直到它检查“/home/john/Downloads/*.xlsx”,此时它被定义为“/home/john/Downloads/CCIE-Collaboration” -v3-Learning-Matrix.xlsx”,因为这是第一个条目。此时 $path 已被定义。它没有机会应用单词扩展(例如,他们是否说在实际路径名定义第一个结果之前将单词扩展应用于“null”)?
我相信他们试图解释的是,在 shell 扩展
/home/john/Downloads/*.xlsx
为/home/john/Downloads/CCIE-Collaboration-v3-Learning-Matrix.xlsx
and/home/john/Downloads/x y.xlsx
(“路径名扩展”)之后,它并没有尝试再次将其拆分为/home/john/Downloads/CCIE-Collaboration-v3-Learning-Matrix.xlsx
,/home/john/Downloads/x
andy.xlsx
(“单词拆分”-而不是“单词扩展”)。所有这些都是在for
循环开始为变量赋值之前进行的。因此,如果要对路径名扩展的结果执行分词,则循环仅迭代两个值,而不是我们会看到的三个值。这些都与循环体内发生的事情无关。另一方面,如果有
for i in $(echo a b c)
,那么 shell 会对命令替换的结果进行分词,并且您会看到三个循环迭代,而不是从 得到的循环迭代for i in "$(echo a b c)"
。当作者编写单词扩展时,他们在页面上的其他任何地方都没有使用这个术语(但我被告知确实完全正确,请参阅此处的POSIX 规范),他们似乎指的是拆分操作,他们称之为“分词”。这是 shell 将根据变量值
$IFS
(通常设置为空格、制表符和换行符)进行拆分的过程。例如,考虑这个脚本(取自这里,这是一个很好的参考,我强烈建议您阅读它):该脚本将简单地打印出其参数,显示它们是如何拆分为单词的。尝试使用不同的输入运行:
在这里,字符串
apple orange "passion fruit"
被分成三个“单词”apple
,orange
和passion fruit
。这就是分词。请注意如何防止引号passion fruit
被拆分。现在,正如文章所述,顺序很重要。如果你阅读bash 手册的“扩展”部分,你会发现:
这里相关的一点是,我上面展示的分词发生在文件名(路径)扩展之前。你的误解在这里:
不,
$path
此时已定义,它不会扩展到/home/john/Downloads/CCIE-Collaboration-v3-Learning-Matrix.xlsx
循环内部,而是在循环开始之前。我们可以使用与您所拥有的稍有不同的脚本来分解这两者,即分词和路径名扩展:在包含以下内容的目录中尝试该脚本:
输出是:
那么发生了什么?
该变量根据 的值
$words
拆分为$IFS
单词。结果是 3 个单词:z
、y.xlsx
和*.xlsx
。接下来,这三个单词中的每一个都进行路径扩展。这使得
x
和y.xlsx
保持不变,因为它们不能扩展到路径,而是*.xlsx
变成x y.xlsx
和CCIE-Collaboration-v3-Learning-Matrix.xlsx
。作者试图表达的观点是,由于在 shell 扩展
*.xlsx
为x y.xlsx
和时已经发生了分词CCIE-Collaboration-v3-Learning-Matrix.xlsx
,因此不会发生进一步的分词,并且x y.xlsx
尽管包含空格,但仍被视为单个单词。请注意,这个答案的第一个版本是错误的,因为我将标记化与分词混淆了。
句子“这意味着单词扩展在路径名扩展之前执行。” 引用中的内容毫无意义,因为来源从未定义“单词扩展”应该是什么。在下一句话中,他们提到了分词,这在页面前面已经描述过,可能就是他们的意思。所以,忽略“单词扩展”这个短语,在这种情况下它是错误的,简单明了。
引用的下一句话更有意义:
考虑以下情况,我们创建两个文件
oneword
并two words
在一个空目录中,然后循环./*
:输出如下,显示循环迭代了 、 两项
./oneword
,./two words
这两项是路径名扩展的直接结果,而后一个文件名没有进一步拆分。如果在路径名扩展之后发生单词拆分,则第二个文件名将进一步拆分为
./two
words
,并且循环将总共运行三次。(假设默认IFS
。)上面的 Q 继续
但正如上面所说,这是没有意义的。您引用的来源没有定义“单词扩展”对他们意味着什么,并且不清楚您对这个短语的含义。这不是你的错:你被一个无法保持其术语一致的来源所困惑。
现在,从技术上讲,POSIX 规范使用短语“单词扩展”来表示所有波浪号扩展、参数扩展、命令替换、算术扩展、字段拆分(通常称为单词拆分)、路径名/文件名扩展和引号删除。但是您的消息来源使用该短语的上下文与该定义没有意义。单词扩展不能发生在路径扩展“之前”,因为单词扩展包括路径扩展。
再次强调,取决于你的意思。POSIX 意义上的“分词”几乎涵盖了所有内容,因此在 Q 中的脚本中,有“单词扩展”(路径名扩展)到
/home/john/Downloads/*.xlsx
文件名,以及“单词扩展”(参数扩展)到"${path}"
当前文件名中。变量的值path
。(我上面的脚本也有算术展开的情况。)