为什么第二个 grep 命令不起作用?

vij*_*yst 1 bash grep

我有一个名为“components”的文件夹,该文件夹中的文件名为“apple”

如果我 cd 到“components”文件夹并执行以下命令:

ls | grep -G a*e
Run Code Online (Sandbox Code Playgroud)

它可以正常工作并正确返回苹果。

但是,如果我不 cd 到 Components 文件夹并执行以下命令:

ls components | grep -G a*e
Run Code Online (Sandbox Code Playgroud)

它不起作用并返回空白。可能是什么原因?

下面的第三个 grep 命令工作正常。

ls components | grep ap
Run Code Online (Sandbox Code Playgroud)

我正在 grep 的实际文件名很复杂。所以我需要grep -G标签才能工作。

ric*_*ici 5

a*e是一个 glob,而不是一个正则表达式。了解其中的差异很重要。

shell 通过将参数与可用文件相匹配来扩展未加引号的参数中的全局变量。in*表示“不包含目录分隔符的任何字符序列”,因此只要该文件存在于当前目录中,a*e它就会匹配文件名apple(或)。accolade.node全局匹配是完整的,而不是子字符串匹配。

grep a*e因此,当您在包含该文件的目录中执行时apple,shell 将在调用 grep 之前替换a*e为该单词apple,从而生成该命令grep apple。如果该目录也包含该文件accolade.node,则 shell 也会将其放入命令行中;grep accolade.node apple。这很少是您希望 grep 参数(文件名参数除外)发生的情况,因此强烈建议养成引用参数的习惯。

与 shell 不同,grep 基于正则表达式匹配。在正则表达式中,*表示“前一个元素的任意次数的重复”,因此正则表达式a*e将匹配e, ae, aae, aaae, 等。由于 grep 进行子字符串匹配(默认情况下),因此这些字符串可以位于匹配行中的任何位置。例如,这将匹配ein ,但它也将匹配包含 的任何其他行,例如。(这让人有点惊讶,没有匹配。也许存在一些打字问题。)appleeelectronicsls components | grep "a*e"components/apple

为了匹配a后面跟着任意字符的序列e,您可以使用正则表达式a.*e(即grep "a.*e"- 请注意使用引号以避免 shell 尝试将该参数扩展为 glob)。但如果你期望它做与 glob 相同的事情,那可能会匹配太多a*e。您可能想要添加一些限制。例如,grep -w 强制匹配完整的单词。并且(至少使用 gnu grep)您可以使用快捷方式(除空格之外的任何字符)来匹配以 开头和结尾grep -w "a\S*e"的完整单词。ae\S

顺便说一句,您很少想使用-G, 特别是因为它是默认值(不幸的是)。大多数时候,您需要使用反斜杠grep -E,以便不必在整个模式中插入反斜杠。请阅读man 7 regex正则表达式语法的快速概述以及基本和扩展 Posix 正则表达式之间的差异。man grep当然也很有用。


jhn*_*hnc 5

不带引号的,是 shell 全局模式,在 grep 运行之前a*e由 shell 扩展。

当您在目录中时,如下:

ls | grep -G a*e
Run Code Online (Sandbox Code Playgroud)

变成

ls | grep -G apple
Run Code Online (Sandbox Code Playgroud)

因为您有一个名为“apple”的文件,所以它匹配。

当您不在该文件夹中时,运行:

ls components | grep -G a*e
Run Code Online (Sandbox Code Playgroud)

shell 再次尝试扩展 glob 模式。

如果当前目录中有任何匹配的文件(例如“abalone”),则 glob 将扩展到该文件。如果有多个这样的文件名(例如,“鲍鱼”、“藻类”),它可能会扩展为多个字符串。命令变成这样:

ls components | grep -G abalone
Run Code Online (Sandbox Code Playgroud)
ls components | grep -G abalone algae
Run Code Online (Sandbox Code Playgroud)

在第一种情况下,除非components目录也包含该文件名,否则您将得到空白输出。

在第二种情况下,grep 将完全忽略该目录并尝试在文件“algae”中查找字符串“abalone”。

还有第三种可能性:glob 找不到任何东西。在这种情况下, grep接收 regexp a*e。grep 的选项-G告诉它使用 BRE 风格的正则表达式。有了这些,a*e意味着“零个或多个a后跟e”。这相当于说“包含e”。

在这种情况下,apple无论您是否参加,您都应该在结果中看到components。在评论中,您说ls components | grep "a*e"没有返回任何内容。由于引用应该得出与第三种情况完全相同的结果,因此这是令人惊讶的。


请注意,如果您打算使用 glob,则根本不需要 grep:

cd components
ls a*e
Run Code Online (Sandbox Code Playgroud)
ls components/a*e
Run Code Online (Sandbox Code Playgroud)