Grep:星号 (*) 并不总是有效

Grep:星号 (*) 并不总是有效

如果我 grep 一个包含以下内容的文档:

ThisExampleString

...对于表达式This*String*String,没有返回任何内容。但是,This*按预期返回了上面的行。

表达式是否用引号括起来并没有区别。

我以为星号表示任意数量的未知字符?为什么它只有在表达式开头时才有效?如果这是预期的行为,我该用什么来代替表达式This*String*String

答案1

星号常用表达表示“匹配前面的元素 0 次或多次”。

在您使用 的特定情况下grep 'This*String' file.txt,您试图说:“嘿,grep,将 单词 匹配给我Thi,然后是小写字母s零次或多次,然后是 单词”。在 中找不到String小写字母,因此 grep 会忽略。sExampleThisExampleString

在 的情况下grep '*String' file.txt,您说的是“grep,匹配单词String”之前的空字符串(实际上什么都没有)。当然,这不是ThisExampleString应该读的方式。(有其他可能的含义——您可以尝试使用或不使用标志-E——但其含义都与您真正想要的不一样。)

知道这.意味着“任何单个字符”,我们可以这样做:grep 'This.*String' file.txt。现在 grep 命令将正确读取它:This后跟任何字符(将其视为 ASCII 字符的选择)重复任意次数,后跟String

答案2

*BRE 1、ERE 1和 PCRE 1中的元字符与先前分组的模式匹配 0 次或多次(如果分组模式位于*元字符之前),与先前字符类匹配 0 次或多次(如果字符类位于*元字符之前),或者与先前字符匹配 0 次或多次(如果元*字符之前既没有分组模式也没有字符类);

这意味着在This*String模式中,由于*元字符前面既没有分组模式也没有字符类,因此它与*前一个字符(在本例中为s字符)的 0 次或多次匹配:

% cat infile               
ThisExampleString
ThisString
ThissString
% grep 'This*String' infile
ThisString
ThissString

要匹配任何字符的 0 次或多次出现,您需要匹配与.任何字符匹配的元字符的 0 次或多次出现:

% cat infile               
ThisExampleString
% grep 'This.*String' infile
ThisExampleString

BRE 和 ERE 中的元字符*始终是“贪婪的”,即它将匹配最长的匹配:

% cat infile
ThisExampleStringIsAString
% grep -o 'This.*String' infile
ThisExampleStringIsAString

这可能不是所需的行为;如果不是,您可以打开grep的 PCRE 引擎(使用选项-P)并附加元字符,当将其放在和元字符?之后时,可以改变它们的贪婪性:*+

% cat infile
ThisExampleStringIsAString
% grep -Po 'This.*?String' infile
ThisExampleString

1:基本正则表达式、扩展正则表达式和 Perl 兼容正则表达式

答案3

其中一个解释在这里找到关联

星号“ *”在正则表达式中的含义与在通配符中的含义不同;它是一个修饰符,适用于前面的单个字符或表达式,例如 [0-9]。星号匹配零个或多个位于其前面的字符。因此,[A-Z]*匹配任意数量的大写字母(包括零个),而[A-Z][A-Z]*匹配一个或多个大写字母。

答案4

*既是外壳,又有特殊意义通配符字符(“通配符”)和正则表达式元字符。你必须同时考虑两者,但如果你引用你的正则表达式,那么你可以阻止 shell 对其进行特殊处理,并确保它将其不变地传递给grep。 虽然有点从概念上讲类似,但*对 shell 的意义与对 的意义却截然不同grep

第一的shell 将其*视为通配符。

你说:

表达式是否用引号括起来并没有区别。

这取决于您运行命令时所在的目录中存在哪些文件。对于包含目录分隔符 的模式/,它可能取决于整个系统中存在哪些文件。您应该始终引用grep--and的正则表达式单引号通常最好——除非你确定你同意九种可能令人惊讶的转变否则,shell 会执行执行grep命令。

当 shell 遇到一个*不是,它表示“零个或多个任意字符”,并且替换包含它的单词包含与模式匹配的文件名列表。(以 开头的文件名将.被排除 - 除非你的模式本身以. 或者你已经将你的 shell 配置为包含它们。)这被称为通配符--还有名字文件名扩展路径名扩展

其效果grep通常是将第一个匹配的文件名作为正则表达式——即使对于人类读者来说,这是很明显的不是表示为正则表达式——而从 glob 中自动列出的所有其他文件名都被视为文件里面在其中搜索匹配项。(您看不到列表 - 它被不透明地传递给grep。)您实际上永远不希望发生这种情况。

原因是有时没问题——至少对于你来说迄今为止,它不是——是*将被单独留下如果以下所有情况都成立

  1. 名称匹配的文件。 ...或者您在 shell 中禁用了通配符,通常使用set -f或等效的set -o noglob。但这种情况并不常见,您可能知道您这样做了。

  2. 您正在使用的 shell 的默认行为是*在没有匹配的文件名时不做任何事情。Bash 就是这种情况,您大概使用,但并非所有 Bourne 风格的 shell 都如此。(例如,流行 shell Zsh 中的默认行为是将 glob 设置为(A)扩展或(二)产生错误。)...或者您已经改变了 shell 的这种行为——不同的 shell 的具体操作有所不同。

  3. 你还没有否则告诉你的 shell 允许将 glob 替换为没有什么在没有匹配文件时,也不会在这种情况下失败并显示错误消息。在 Bash 中,可以通过启用nullglobfailglob shell 选项, 分别。

有时您可以依赖 #2 和 #3,但很少可以依赖 #1。grep现在可以使用的不带引号模式的命令可能会在您拥有不同的文件或从不同位置运行它时停止工作。引用你的正则表达式,问题就解决了。

然后grep命令将其*视为量词。

其他答案——比如那些作者 Sergiy Kolodyazhnyy来自 kos-- 也以不同的方式解决了这个问题的这个方面。因此,我鼓励那些还没有读过这些的人在阅读本回答的其余部分之前或之后阅读它们。

假设确实*进入了 grep(引用应该确保这一点),grep那么就意味着它之前的项可能发生任意次,而不必恰好发生一次。它可能仍会出现一次。或者它可能根本不存在。或者它可能重复出现。符合任何这些可能性将会被匹配。

我所说的“项目”是什么意思?

  • 单一特点。由于b匹配文字bb*匹配零个或多个,b因此ab*c匹配ac,,,,等abcabbcabbbc

    同样地,由于.匹配任意字符,.*匹配零个或多个字符1,因此a.*c匹配acakcahjglhdfjkdlgjdfkshlgc,甚至acccccchjckhcc,等等。或者

  • A字符类。由于匹配[xy]xy匹配零个或多个字符,[xy]*其中每个字符是x或,y因此p[xy]*q匹配pq,,,,,,,,,,等。pxqpyqpxxqpxyqpyxqpyyqpxxxqpxxyq

    这也适用于简写形式字符类,如\w\W\s\S。由于\w匹配任何单词字符,\w*因此 匹配零个或多个单词字符。或者

  • A团体. 由于\(bar\)匹配bar\(bar\)*匹配零个或多个bar,因此foo\(bar\)*baz匹配foobazfoobarbazfoobarbarbazfoobarbarbarbaz等。

    使用-E-P选项,grep将正则表达式视为外部环境影响因子或者聚合酶链反应而不是作为布瑞雅,然后组被( )代替包围\( \),因此您可以使用(bar)代替\(bar\)foo(bar)baz代替foo\(bar\)baz

man grep在最后给出了 BRE 和 ERE 语法的合理易懂的解释,并grep在开头列出了所有命令行选项。我推荐该手册页作为资源,并且GNU Grep 文档本教程/参考网站(我已将其链接到上面的多个页面)。

为了测试和学习grep,我建议使用模式而不是文件名来调用它。然后它从您的终端获取输入。输入行;回显给您的行是包含您的模式匹配的文本的行。要退出,请在行首按Ctrl+ ,这表示输入结束。(或者您可以像大多数命令行程序一样按+ 。)例如:DCtrlC

grep 'This.*String'

如果你使用--color标志,grep将突出显示特定部分匹配正则表达式的行,这对于弄清正则表达式的作用以及找到您要查找的内容都非常有用。默认情况下,Ubuntu 用户有一个 Bash 别名,grep --color=auto当您从命令行运行时,它会导致运行 - 这对于此目的已经足够了grep,因此您甚至可能不需要--color手动传递。

1 因此,.*正则表达式中的含义*与 shell glob 中的含义相同。但是,不同之处在于会grep自动打印包含匹配项的行任何地方因此,通常不需要将其放在.*正则表达式的开头或结尾。

相关内容