测试单词扩展和路径名扩展的简单脚本

测试单词扩展和路径名扩展的简单脚本

我正在尝试验证我读到的以下内容这里:

重要的是要记住,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”)?

答案1

当作者写道单词扩展,他们在页面上的其他任何地方都没有使用这一术语(但据我所知,这个术语确实完全正确,请参阅 POSIX 规范这里),他们似乎指的是拆分操作,他们称之为“分词”。这是 shell 将根据变量值$IFS(通常设置为空格、制表符和换行符)进行拆分的过程。例如,考虑这个脚本(取自这里,这是一个很好的参考,我强烈建议您阅读它):

#!/bin/sh -
printf "%d args:" "$#"
[ "$#" -eq 0 ] || printf " <%s>" "$@"
echo

该脚本将简单地打印出其参数,显示它们是如何拆分为单词的。尝试使用不同的输入运行:

$ foo.sh apple orange "passion fruit"
3 args: <apple> <orange> <passion fruit>

在这里,字符串apple orange "passion fruit"被分成三个“单词” apple,orangepassion fruit。这就是分词。请注意如何防止引号passion fruit被拆分。

现在,正如文章所述,顺序很重要。如果您阅读了“扩展”部分bash手册, 你会找到:

展开的顺序是:大括号展开;波形符扩展、参数和变量扩展、算术扩展和命令替换(以从左到右的方式完成);分词;和文件名扩展。

这里相关的一点是,我上面展示的分词发生在文件名(路径)扩展之前。你的误解在这里:

为了澄清,如果我们运行第一次迭代,$path 是空的,直到它检查“/home/john/Downloads/*.xlsx”,此时它被定义为“/home/john/Downloads/CCIE-Collaboration” -v3-Learning-Matrix.xlsx”,因为这是第一个条目。此时 $path 已被定义。

不,$path此时已定义,它不会扩展到/home/john/Downloads/CCIE-Collaboration-v3-Learning-Matrix.xlsx循环内部,而是在循环开始之前。我们可以使用与您所拥有的稍有不同的脚本来分解这两者,即分词和路径名扩展:

#!/bin/bash

words="x y.xlsx *.xlsx"
for word in $words; do
  echo "word is '$word'"
done

在包含以下内容的目录中尝试该脚本:

$ ls -1
CCIE-Collaboration-v3-Learning-Matrix.xlsx
'x y.xlsx'

输出是:

$ foo.sh
word is 'x'
word is 'y.xlsx'
word is 'CCIE-Collaboration-v3-Learning-Matrix.xlsx'
word is 'x y.xlsx'

那么发生了什么?

  1. 变量$words分裂转化为文字的价值$IFS。结果是 3 个单词:zy.xlsx*.xlsx

  2. 接下来,这三个单词中的每一个都进行路径扩展。这使得xy.xlsx保持不变,因为它们不能扩展到路径,而是*.xlsx变成x y.xlsxCCIE-Collaboration-v3-Learning-Matrix.xlsx

作者试图表达的观点是,由于在 shell 扩展*.xlsxx y.xlsx和时已经发生了分词CCIE-Collaboration-v3-Learning-Matrix.xlsx,因此不会发生进一步的分词,并且x y.xlsx尽管包含空格,但仍被视为单个单词。


请注意,这个答案的第一个版本是错误的,因为我很困惑代币化与分词。

答案2

我相信他们试图解释的是,在 shell 扩展/home/john/Downloads/*.xlsx/home/john/Downloads/CCIE-Collaboration-v3-Learning-Matrix.xlsxand /home/john/Downloads/x y.xlsx(“路径名扩展”)之后,它并没有尝试再次将其拆分为/home/john/Downloads/CCIE-Collaboration-v3-Learning-Matrix.xlsx, /home/john/Downloads/xand y.xlsx(“分词”-不是“单词扩展”)。所有这些都是在for循环开始为变量赋值之前进行的。因此,如果要对路径名扩展的结果执行分词,则循环仅迭代两个值,而不是我们会看到的三个值。这些都与循环体内发生的事情无关。

另一方面,如果有for i in $(echo a b c),那么 shell 会对命令替换的结果进行分词,并且您会看到三个循环迭代,而不是从 得到的循环迭代for i in "$(echo a b c)"

答案3

这句话“这意味着单词扩展是在路径名扩展之前执行的。”引用中的内容毫无意义,因为来源从未定义“单词扩展”应该是什么。在下一句话中,他们提到了分词,这在页面前面已经描述过,可能就是他们的意思。所以,忽略“单词扩展”这个短语,在这种情况下它是错误的,简单明了

引用的下一句话更有意义:

因此,如果您循环遍历扩展路径的结果,则不会对这些结果执行分词。

考虑以下情况,我们创建两个文件onewordtwo words在一个空目录中,然后循环./*

touch 'oneword' 'two words'
i=0 
for f in ./*; do
    printf "%d: %s\n" "$i" "$f"
    i=$(( i+1 ))
done

输出如下,显示循环迭代了 、 两项./oneword./two words这两项是路径名扩展的直接结果,而后一个文件名没有进一步拆分。

0: ./oneword
1: ./two words

如果发生分词路径名扩展,第二个文件名将进一步拆分为./two words,并且循环将总共运行三次。 (假设默认IFS。)


上面的 Q 继续

据我所知,单词扩展发生在路径扩展之前,他们使用了一个与此非常相似的脚本来演示它。

但正如上面所说,这是没有意义的。您引用的来源没有定义“单词扩展”对他们意味着什么,并且不清楚您对这个短语的含义。这不是你的错:你被一个无法保持其术语一致的来源所困惑。

现在,从技术上来说,POSIX规范对所有波形符扩展、参数扩展、命令替换、算术扩展、字段拆分(通常称为单词拆分)、路径名/文件名扩展和引号删除使用短语“单词扩展”。但是您的消息来源使用该短语的上下文与该定义没有意义。单词扩展不能发生在路径扩展“之前”,因为单词扩展包括路径扩展。

那么,地球这个词的扩展会出现在哪里呢,因为我看不到任何可以考虑扩展的词语。

再次强调,取决于你的意思。 POSIX 意义上的“分词”几乎涵盖了所有内容,因此在 Q 中的脚本中,有“单词扩展”(路径名扩展)到/home/john/Downloads/*.xlsx文件名,以及“单词扩展”(参数扩展)到"${path}"当前文件名中。变量的值path。 (我上面的脚本也有算术展开的情况。)

相关内容