为什么我必须在 grep 的正则表达式中引用转义字符,而不是在在线正则表达式引擎上?

为什么我必须在 grep 的正则表达式中引用转义字符,而不是在在线正则表达式引擎上?

我确信这个问题的某些版本之前已经被问过并得到回答,但我环顾四周并没有找到确切的答案。也许这里有人可以帮助我点亮灯泡。我使用的是 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-009500.

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 的转义字符。\.与外壳相同'.'echoset -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-Fgrep模式视为固定字符串而不是正则表达式,因此它只会返回具有该确切字符串的行。因此,您甚至不需要转义.或使用引号,因为它只会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 正则表达式或类似工具,它们具有标准正则表达式中没有的许多功能。

看:

相关内容