![为什么我必须在 grep 的正则表达式中引用转义字符,而不是在在线正则表达式引擎上?](https://linux22.com/image/172190/%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E5%BF%85%E9%A1%BB%E5%9C%A8%20grep%20%E7%9A%84%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E4%B8%AD%E5%BC%95%E7%94%A8%E8%BD%AC%E4%B9%89%E5%AD%97%E7%AC%A6%EF%BC%8C%E8%80%8C%E4%B8%8D%E6%98%AF%E5%9C%A8%E5%9C%A8%E7%BA%BF%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E5%BC%95%E6%93%8E%E4%B8%8A%EF%BC%9F.png)
我确信这个问题的某些版本之前已经被问过并得到回答,但我环顾四周并没有找到确切的答案。也许这里有人可以帮助我点亮灯泡。我使用的是 Mojave 10.14.6 和 bash 3.2.57(1)-release 的 Mac。
我正在通过在线教程学习正则表达式的基础知识,并在在线网站上进行练习https://regexr.comgrep
,并在我的本地计算机上的 bash 中使用。
我正在使用一个小文本文件(称为small.txt)进行练习,其中包含三件事:
9.00
9-00
9500
我知道.
通配符将匹配该位置的任何一个字符。因此,在我使用的在线正则表达式引擎(JavaScript)中, /9.00/g
将匹配所有三个字符串9.00
9-00
和9500
.
grep
如果我在命令行上使用,效果是一样的:
~/bin $ grep 9.00 small.txt
9.00
9-00
9500
到目前为止,一切都很好。该教程说,要将.
元字符转换为文字,您必须转义它。好的。因此,按照预期,放入/9\.00/g
在线正则表达式框中只会匹配9.00
,而不是 9-00 或 9500。太棒了。
但是,如果我在命令行中输入相同的语法grep
,则会得到意外的结果:
~/bin $ grep 9\.00 small.txt
9.00
9-00
9500
和之前一样。为了开始grep
工作,我要么必须双引号整个字符串:
~/bin $ grep "9\.00" small.txt
9.00
或者只是双引号转义字符:
~/bin $ grep 9"\."00 small.txt
9.00
我很可能可以做出一些其他的引用选择,这也会给我正确的结果。
这让我很难理解正则表达式的基础知识,因为很明显,我首先必须了解如何grep
在 shell 中不同来自传统的正则表达式语法。学习正则表达式的所有规则已经够难的了,但是当你添加经典正则表达式和 bash shell 行为之间的差异时,我的头就爆炸了。
不管怎样,想知道是否有一个明确的解释可以帮助我解决这个问题,并让我走上正确学习正则表达式的道路,我可以在命令行上与 grep 一起使用这些正则表达式。
(关于正则表达式的课程都没有指出 grep 与 bash 的命令行版本与您在在线正则表达式测试器上看到的“纯”正则表达式语法之间的差异。)我知道引擎之间存在差异高级水平,但这似乎是非常基础的东西,我觉得我一定错过了一些东西。
谢谢。
答案1
为什么?因为您的 shell 解释一些特殊字符,例如\
您的示例中的字符。
您遇到了麻烦,因为您没有保护尝试通过 Shell 作为参数传递给 grep 的字符串。
几种解决方案:
- 单引号字符串,
- 双引号字符串(使用双引号,shell 将解释一些事情,例如
$variables
,在将结果字符串发送到命令之前), - 或者不使用引号(我强烈建议不要这样做),但在正确的位置添加反斜杠,以防止 shell 在将其发送到命令之前解释下一个字符。
我建议通过单引号保护字符串,因为它几乎保留了所有内容:
grep '9\.0' #send those 4 characters to grep in a single argument
Shell 按字面意思传递单引号字符串。
注意:唯一不能包含在单引号 shell 字符串中的是单引号(因为这会结束单引号)。要在单引号 shell 字符串中包含单引号,您需要首先结束单引号,立即添加转义单引号
\'
(或双引号之间的一个"'"
:),然后立即重新输入单引号以继续单引号字符串:例如让 shell 执行命令grep a'b
,您可以将参数写为,'a'\''b'
以便 shell 发送a'b
到 grep: 所以写:grep 'a'\''b'
,或者grep 'a'"'"'b'
如果您坚持不使用引号,则您的 shell 需要有 a\\
才能将 a 发送\
到 grep。
grep 9\\.0 # ie: a 9, a pair \\, a ., and a 0 , and the shell interprets the pair \\ into a literal \
如果您使用双引号:您需要考虑到 shell 将首先解释几件事($vars
、\
等)。例如,当它看到未转义或未引用的 时\
,它会等待下一个字符来决定如何解释它。\w
被视为单个字母w
,\\
被视为单个字母\
,等等。
grep "9\\.0" # looks here the same as not quoting at all...
#but doublequoting allows you to have spaces, etc, inside the string
答案2
将评论转化为答案:
问题是这\
是正则表达式和 shell 的转义字符。\.
与外壳相同'.'
。echo
并set -x
帮助理解 shell 的作用:
> echo \.
.
> echo '\.'
\.
> echo \\.
\.
> set -x
> echo 9_00 | grep 9\.00
+ echo 9_00
+ grep 9.00
9_00
因此,如果命令应该看到 ,\
则必须用引号或第二个 来保护它\
。
答案3
要添加其他答案和评论,您可以做的另一件事grep
是使用以下命令来返回您想要的内容:
grep -F 9.00 small.txt
输出:
9.00
make-F
将grep
模式视为固定字符串而不是正则表达式,因此它只会返回具有该确切字符串的行。因此,您甚至不需要转义.
或使用引号,因为它只会9.00
完全匹配,而不是将 视为.
任何字符。
答案4
为什么我必须在 grep 的正则表达式中引用转义字符,而不是在在线正则表达式引擎上?
你不必引用它grep,但对于外壳。
使用grep -f
从文件中读取模式表明,9\.00
您显示的模式在不通过 shell 传递时工作正常。
$ cat re.txt
9\.00
$ grep -f re.txt small.txt
9.00
事实上,问题grep
本身并不是问题,这可能就是您在有关正则表达式的文章中看不到它的原因。不过,您可能会在一篇有关 shell 如何工作的文章中看到相关要点......
我知道高级发动机之间存在差异
甚至不必太先进。+
BRE 和 ERE 中的类似功能已经有所不同。另外,至少一些在线工具默认使用 Perl 正则表达式或类似工具,它们具有标准正则表达式中没有的许多功能。
看: