您可以有时省略引号,但最安全的做法是不要。这是因为正则表达式的语法与文件名通配符模式的语法重叠,并且当 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 扩展,如 here 和 here)。重要的是要知道您使用的工具可以理解哪种变体,并将您的正则表达式写入该语法。
这是一个简单的例子:“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 简写。