我最近在命令行上使用一些正则表达式时遇到了麻烦,发现为了匹配反斜杠,可以使用不同数量的字符。该数字取决于正则表达式使用的引号(无、单引号、双引号)。请参阅以下 bash 会话以了解我的意思:
echo "#ab\\cd" > file
grep -E ab\cd file
grep -E ab\\cd file
grep -E ab\\\cd file
grep -E ab\\\\cd file
#ab\cd
grep -E ab\\\\\cd file
#ab\cd
grep -E ab\\\\\\cd file
#ab\cd
grep -E ab\\\\\\\cd file
#ab\cd
grep -E ab\\\\\\\\cd file
grep -E "ab\cd" file
grep -E "ab\\cd" file
grep -E "ab\\\cd" file
#ab\cd
grep -E "ab\\\\cd" file
#ab\cd
grep -E "ab\\\\\cd" file
#ab\cd
grep -E "ab\\\\\\cd" file
#ab\cd
grep -E "ab\\\\\\\cd" file
grep -E 'ab\cd' file
grep -E 'ab\\cd' file
#ab\cd
grep -E 'ab\\\cd' file
#ab\cd
grep -E 'ab\\\\cd' file
这意味着:
- 如果没有引号,我可以将一个反斜杠与 4-7 个实际反斜杠相匹配
- 使用双引号,我可以将反斜杠与 3-6 个实际反斜杠匹配
- 使用单引号,我可以将反斜杠与 2-3 个实际反斜杠匹配
据我所知,shell 会忽略一个额外的反斜杠(来自 bash 手册页):
“不带引号的反斜杠 (\) 是转义字符。它保留后面的下一个字符的字面值”
这不适用于单引号示例,因为单引号中不进行转义。
grep 命令会忽略一个额外的反斜杠(“\c”只是“c”转义,但这与“c”相同,因为“c”在正则表达式中没有特殊含义)。
这解释了带有单引号的示例的行为,但我不太理解其他两个示例,特别是为什么非引用的双引号字符串之间存在差异。
再次引用 bash 手册页中的一段话:
“将字符括在双引号中会保留引号内所有字符的字面值,但 $、`、\ 和启用历史扩展时的 ! 除外。”
我尝试使用 GNU awk (例如) 进行相同操作awk /ab\cd/{print} file
,结果相同。
然而,Perl 显示了不同的结果(使用例如perl -ne "/ab\\cd/"\&\&print file
):
- 如果没有引号,我可以将反斜杠与 4-5 个实际反斜杠匹配
- 使用双引号,我可以将反斜杠与 3-4 个实际反斜杠匹配
- 使用单引号,我可以将一个反斜杠与 2 个实际的反斜杠相匹配
谁能解释 grep 和 awk 命令行上非引号和双引号正则表达式字符串之间的区别?我对 Perl 行为的解释不太感兴趣,因为我通常不使用 Perl 的单行代码。
答案1
对于未加引号的示例,每一\\
对将一个反斜杠传递给 grep,因此 4 个反斜杠将两个反斜杠传递给 grep,这会转换为单个反斜杠。 6 个反斜杠将 3 个传递给 grep,转换为 1 个反斜杠和 1 个\c
,等于c
。多一个反斜杠不会改变任何内容,因为它是由 shell翻译\c
-> 的。 c
shell 中的八个反斜杠在 grep 中是四个,转换为两个,所以这不再匹配。
对于双引号中的示例,请注意 bash 手册页中第二个引用后面的内容:
仅当反斜杠后跟以下字符之一时,反斜杠才保留其特殊含义:$、`、"、\ 或换行符。
即,当您给出奇数个反斜杠时,序列以 结尾\c
,这在未加引号的情况下等于c
,但当加引号时,反斜杠将失去其特殊含义,因此\c
会传递给 grep。这就是为什么“可能的”反斜杠(即构成与示例文件匹配的模式的反斜杠)的范围下降了一个。
答案2
此链接描述了 bash引号和转义
您的问题涉及前三个部分。
- 每个字符转义
- 弱引用 “双引号”
- 强引用 '单引号'
- 类似 ANSI C 的字符串引用
- I18N/L10N 引用(国际化和本地化)。
下面的图表显示了字符串如何bash
传递它们grep
以及如何grep
在内部进一步解释它们。
我们先来看一下echo "#ab\\cd" > file
。
在里面弱引用("") "#ab\\cd"
, \\
是一个转义,它作为单个文字\
传递。所以,包含 file
\
file
ab\cd
现在,按照您的命令:下面的图表可能有助于了解每次调用的实际情况。显示与*
文件内容匹配的内容。这实际上只是应用 bash 的转义规则的问题,就像在网页上一样,特别注意丹尼尔·库尔曼的回答他提到的逃避行为弱引用情况。
仅当反斜杠后跟以下字符之一时,反斜杠才保留其特殊含义:$、`、"、\ 或换行符。
bash passes grep further
to grep resolves to
grep -E ab\cd file abcd abcd
grep -E ab\\cd file ab\cd abcd
grep -E ab\\\cd file ab\cd abcd
grep -E ab\\\\cd file ab\\cd ab\cd *
grep -E ab\\\\\cd file ab\\\cd ab\cd *
grep -E ab\\\\\\cd file ab\\\cd ab\cd *
grep -E ab\\\\\\\cd file ab\\\cd ab\cd *
grep -E ab\\\\\\\\cd file ab\\\\cd ab\\cd
grep -E "ab\cd" file ab\cd abcd
grep -E "ab\\cd" file ab\cd abcd
grep -E "ab\\\cd" file ab\\cd ab\cd *
grep -E "ab\\\\cd" file ab\\cd ab\cd *
grep -E "ab\\\\\cd" file ab\\\cd ab\cd *
grep -E "ab\\\\\\cd" file ab\\\cd ab\cd *
grep -E "ab\\\\\\\cd" file ab\\\\cd ab\\cd
grep -E 'ab\cd' file ab\cd abcd
grep -E 'ab\\cd' file ab\\cd ab\cd *
grep -E 'ab\\\cd' file ab\\\cd ab\cd *
grep -E 'ab\\\\cd' file ab\\\\cd ab\\cd