dan*_*ann 13 command-line shell quoting regular-expression
我最近在命令行上遇到了一些正则表达式的问题,发现为了匹配反斜杠,可以使用不同数量的字符。这个数字取决于用于正则表达式的引用(无、单引号、双引号)。请参阅以下 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
Run Code Online (Sandbox Code Playgroud)
这意味着:
我知道 shell 会忽略一个额外的反斜杠(来自 bash 手册页):
“一个不带引号的反斜杠 (\) 是转义字符。它保留了下一个字符的字面值”
这不适用于单引号示例,因为单引号中没有进行转义。
并且 grep 命令忽略了一个额外的反斜杠(“\c”只是“c”转义,但这与“c”相同,因为“c”在正则表达式中没有特殊含义)。
这用单引号解释了示例的行为,但我不太了解其他两个示例,尤其是为什么非引用字符串和双引号字符串之间存在差异。
再次引用来自 bash 手册页的引用:
“用双引号括起来的字符会保留引号内所有字符的字面值,除了 $、`、\,并且在启用历史扩展时,!。”
我对 GNU awk(例如awk /ab\cd/{print} file)进行了同样的尝试,结果相同。
然而,Perl 显示不同的结果(使用 eg perl -ne
"/ab\\cd/"\&\&print file):
任何人都可以解释 grep 和 awk 命令行上非引号和双引号正则表达式字符串之间的区别吗?我对解释 Perl 的行为并不感兴趣,因为我通常不使用 Perl 单行程序。
Ans*_*ann 11
对于未加引号的示例,每一\\对都将一个反斜杠传递给 grep,因此 4 个反斜杠将两个反斜杠传递给 grep,从而转化为单个反斜杠。6 个反斜杠将三个传递给 grep,转换为一个反斜杠和一个\c,等于c. 一个额外的反斜杠不会改变任何东西,因为它是由 shell翻译\c->c的。shell 中的八个反斜杠在 grep 中是四个,转换为两个,因此不再匹配。
对于双引号中的示例,请注意 bash 手册页中第二个引号后面的内容:
反斜杠仅在后跟以下字符之一时保留其特殊含义:$、`、"、\ 或换行符。
即,当您给出奇数个反斜杠时,序列以 结尾\c,这c在不带引号的情况下等于,但是当引用时,反斜杠失去了它的特殊含义,因此\c传递给 grep。这就是为什么“可能的”反斜杠范围(即那些构成与示例文件匹配的模式的)向下滑动一个的原因。
此链接描述了 bash引号和转义
您的问题涉及前三个部分。
下面是字符串如何bash传递给它们grep以及如何在grep内部进一步解释它们的图表。
我们先来看看echo "#ab\\cd" > file。
在弱引用("") 中"#ab\\cd", the \\是一个转义的\,它file作为单个文字传递给\。所以,file包含 ab\cd
现在,对于您的命令:下表可能有助于了解每次调用的实际情况。该*显示匹配文件内容的人。这实际上只是应用 bash 的转义规则的问题,就像在网页上一样,特别要注意daniel kullmann 的回答,他指的是在弱引用情况下的转义行为。
反斜杠仅在后跟以下字符之一时保留其特殊含义:$、`、"、\ 或换行符。
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
Run Code Online (Sandbox Code Playgroud)