【问题标题】:Difference between using grep regex pattern with or without quotes?使用带引号或不带引号的 grep 正则表达式模式之间的区别?
【发布时间】:2020-01-09 20:28:58
【问题描述】:

我正在 Linux Academy 学习,教程展示了如何使用 grep 和 regex。

他将他的正则表达式模式放在引号之间,如下所示:

grep 'pattern' file.txt

这似乎和不加引号一样:

grep pattern file.txt 

但是当他做这样的事情时,他需要转义{和}:

grep '^A\{1,4\}' file.txt 

在进行一些测试后,在编写不带引号的模式时似乎不需要这些转义字符。

grep ^A{1,4} file.txt

那么这两种方法有什么区别呢? 报价单有必要吗? 为什么在第一种情况下需要转义字符?

最后,我还看到了其他方法,例如 grep -E 和 egrep,这是人们使用 regex 进行 grep 最常用的方法?

编辑:感谢您提醒我们模式在文件之前。

非常感谢!

【问题讨论】:

    标签: regex bash syntax grep


    【解决方案1】:

    您可以有时省略引号,但最安全的做法是不要。这是因为正则表达式的语法与文件名通配符模式的语法重叠,并且当 shell 看到看起来像通配符模式的东西(并且它不在引号中)时,shell 会尝试将其“扩展”成一个列表匹配的文件名。如果没有匹配的文件,则原封不动地通过,但如果有匹配,则将其替换为匹配的文件名。

    这是一个简单的例子。假设我们正在尝试在 file.txt 中搜索“a”,然后是一些“b”,然后只打印匹配项。所以你运行:

    grep -o ab* file.txt
    

    现在,“ab* 可以解释为查找以“ab”开头的文件的通配符模式,而 shell 会以这种方式解释它。如果当前没有文件以“ab”开头的目录,这不会造成问题。但是假设有两个,“abcd.txt”和“abcdef.jpg”。然后shell将其扩展为等价于:

    grep -o abcd.txt abcdef.jpg file.txt
    

    ...然后grep 将在文件abcdef.jpg 和file.txt 中搜索正则表达式模式abcd.txt

    因此,基本上,使用不带引号的正则表达式模式可能有效,但不安全。所以不要这样做。

    顺便说一句,我还建议使用单引号而不是双引号,因为有些正则表达式字符即使在双引号中也会被 shell 特殊处理(主要是美元符号和反斜杠/逃脱)。同样,它们通常会原封不动地通过,但并非总是如此,除非您了解(有些混乱的)解析规则,否则您可能会得到意想不到的结果。

    顺便说一句^2,出于类似的原因,您应该(几乎)总是在变量引用周围加上双引号(例如grep -O 'ab* "$filename" 而不是grep -O 'ab*' $filename)。单引号根本不允许变量引用;不带引号的变量引用会受到分词和通配符扩展的影响,这两者都会造成麻烦。双引号变量得到扩展,没有别的

    BTW^3,正则表达式语法有很多变体。示例表达式中的花括号需要转义的原因是,默认情况下,grep 使用POSIX "basic" regular expression syntax ("BRE")。在 BRE 语法中,某些正则表达式特殊字符(包括大括号和圆括号)必须转义以具有其特殊含义(而其他一些字符,例如与 | 的交替,则根本不可用)。另一方面,grep -E 使用“扩展”正则表达式语法 (“ERE”),其中这些字符具有其特殊含义,除非它们被转义。

    还有与 Perl 兼容的语法 (PCRE) 和许多其他变体。使用错误的语法变体是正则表达式出现问题的常见原因(例如,在 ERE 上下文中使用 perl 扩展,如 herehere)。重要的是要知道您使用的工具可以理解哪种变体,并将您的正则表达式写入该语法。

    这是一个简单的例子:“a”,后跟 1 到 3 个类似空格的字符,然后是“b”,在各种正则表达式语法变体中:

    a[[:space:]]\{1,3\}b    # BRE syntax
    a[[:space:]]{1,3}b      # ERE syntax
    a\s{1,3}b               # PCRE syntax
    

    只是为了让事情变得更复杂,一些工具名义上会接受一种语法,但也允许从其他语法变体中进行一些扩展。在上面的示例中,您可以看到 perl 为类似空格的字符添加了简写 \s,这不是 POSIX 标准语法的一部分。但实际上许多名义上使用 BRE 或 ERE 的工具实际上会接受 \s 简写。

    【讨论】:

    • 感谢您的精彩解释!但现在我想知道为什么我需要转义 { 和 } 才能工作?转义它们不意味着使用文字字符而不是表达式吗?
    • @GordonDavisson:伟大的 cmets!您应该将它们添加到答案中。
    • @Navaro 完成,我添加了一个示例来展示其中的一些差异。
    • @AlvaroBataller 我在答案中添加了对转义的解释。
    【解决方案2】:

    实际上,您的问题中有两个完全不相关的转义方面。首先要做的是如何在 bash 中表示字符串。这是关于可读性,这通常意味着个人品味。例如,我不喜欢转义,因此我更喜欢将ab\ cd 写成'ab cd'。因此,我会写

    echo 'ab cd'
    grep -F 'ab cd' myfile.txt
    

    而不是

    echo ab\ cd
    grep -F ab\ cd myfile.txt
    

    但是任何一个都没有问题,你可以选择你觉得更简单的那个。

    另一方面确实grep相关,至少只要你不使用grep中的-F选项,它总是解释搜索论据字面意思。在这种情况下,不涉及 shell,问题是某个字符被解释为正则表达式字符还是文字。 Gordon Davisson 已经详细解释了这一点,所以我只举一个结合两个方面的例子:

    假设您想用 grep 搜索一个空格,然后是一个或多个句点,然后是另一个空格。你不能这样写

    grep -E  .+  myfile.txt
    

    因为空格会被 bash 吃掉,而.grep 有特殊的意义。因此,您必须选择一些逃生机制。我的个人风格是

    grep -E ' [.]+ ' myfile.txt
    

    但许多人不喜欢[.] 而更喜欢\.。这将成为

    grep -E ' \.+ ' myfile.txt
    

    这仍然使用引号从 shell 中挽救空格,但转义了 grep 的句点。如果你更喜欢不使用引号,你可以写

    grep -E \ \\.+\  myfile.txt
    

    请注意,您需要在用于 grep 的 \ 前面加上另一个 \,因为反斜杠与空格一样,对 shell 有特殊含义,如果您不写 \\., grep 不会看到反斜杠句点,而只是一个句点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多