在命令行上转义正则表达式反斜杠所需的反斜杠数


12

我最近在命令行上遇到一些正则表达式时遇到麻烦,发现为了匹配反斜杠,可以使用不同数量的字符。此数字取决于用于正则表达式的引号(无,单引号,双引号)。请参阅以下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单行代码。

Answers:


10

对于未引用的示例,每\\对将一个反斜杠传递给grep,因此4个反斜杠将两个传递给grep,这意味着一个反斜杠。6个反斜杠将三个传递给grep,表示一个反斜杠和一个反斜杠,\c等于c。另外一个反斜杠不会更改任何内容,因为它是由Shell 转换\c-> c的。外壳程序中的八个反斜杠在grep中为四个,转换为两个,因此不再匹配。

对于双引号示例,请注意bash联机帮助页中第二个引号后面的内容:

仅当反斜杠后面跟随以下字符之一时,才保留其特殊含义:$,`,“,\或换行符。

也就是说,当您给出奇数个反斜杠时,序列以结束\c,这c与未引用的情况相同,但是当被引用时,反斜杠失去了其特殊含义,因此\c被传递给grep。这就是为什么“可能的”反斜杠(即组成与示例文件匹配的模式的反斜杠)的范围向下滑动一位的原因。


...然后有一些奇怪的地方:例如:printf "\ntest"即使"\n"应该已经被"n"shell 转换为双引号,但应该在外壳“ test”之前插入换行符(因此,预期结果应该是,对于“ \ ntest”,“ ntest”。我们应该养成写的习惯:printf "\\ntest"printf '\ntest',但是不知何故,我看到很多脚本依赖于怪异性
奥利维尔·杜拉克

6

此链接描述了bash 行情和转义

您的问题涉及前三个部分。

  • 每个字符的转义
  • 弱引用 “双引号”
  • 强引用 “单引号”
  • 类似于ANSI C的字符串引用
  • I18N / L10N报价(国际化和本地化)

下面的图表说明了字符串如何bash传递给它们grep以及如何在grep内部进一步解释它们。

让我们先来看一下echo "#ab\\cd" > file
弱引号(“”)中"#ab\\cd"\\是转义符\,将其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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.