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

dba*_*tes 11 grep bash regular-expression

我确定这个问题的某些版本之前已经被问过和回答过,但我环顾四周并没有找到确切的答案。也许这里有人可以帮我点亮灯泡。我在 Mac 上使用 Mojave 10.14.6 和 bash 3.2.57(1)-release。

我正在通过在线教程学习正则表达式的基础知识,并在在线网站https://regexr.com上练习,并grep在我的本地机器上使用bash。

我正在练习一个包含三个内容的小文本文件(称为 small.txt):

9.00
9-00
9500
Run Code Online (Sandbox Code Playgroud)

我知道.通配符将匹配该位置的任何一个字符。因此,在我使用的在线正则表达式引擎 (JavaScript) 中, /9.00/g将匹配所有三个字符串9.00 9-009500.

如果我grep在命令行上使用也是一样的:

~/bin $ grep 9.00 small.txt
9.00
9-00
9500
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好。教程说,要将.元字符转换为文字,您必须对其进行转义。好的。所以放入/9\.00/g在线正则表达式框只会匹配9.00,正如预期的那样,而不是 9-00 或 9500。太好了。

但是,如果我grep在命令行中输入相同的语法,则会得到意想不到的结果:

~/bin $ grep 9\.00 small.txt
9.00
9-00
9500
Run Code Online (Sandbox Code Playgroud)

和之前一样。为了开始grep工作,我要么必须双引号整个字符串:

~/bin $ grep "9\.00" small.txt
9.00
Run Code Online (Sandbox Code Playgroud)

或者只是双引号转义字符:

~/bin $ grep 9"\."00 small.txt
9.00
Run Code Online (Sandbox Code Playgroud)

很可能还有其他一些我可以做出的引用选择,这也会给我正确的结果。

这让我很难理解正则表达式的基础知识,因为很明显,我首先必须了解grepshell传统的正则表达式语法有何不同。学习正则表达式的所有规则已经够难的了,但是当您添加经典正则表达式和 bash shell 行为之间的差异时,我的头就炸了。

无论如何,想知道是否有一个明确的解释可以为我澄清这一点,并让我走上正确学习正则表达式的道路,我可以在命令行上与 grep 一起使用。

(关于正则表达式的课程都没有指出带有 bash 的 grep 命令行版本与您在在线正则表达式测试人员上看到的“纯”正则表达式语法之间的差异。)我知道引擎之间存在差异高级水平,但这似乎是非常基本的东西,我觉得我一定错过了一些东西。

谢谢。

Oli*_*lac 14

为什么?因为您的 shell 会解释一些特殊字符,例如\在您的示例中。

您遇到了麻烦,因为您没有保护您尝试通过 Shell 作为参数传递给 grep 的字符串。

几种解决方案:

  • 单引号字符串,
  • 双引号字符串($variables在将结果字符串发送到命令之前,shell 会解释一些事情,例如,
  • 或者不使用引号(我强烈建议不要使用),而是在正确的位置添加反斜杠以防止 shell 在将其发送到命令之前解释下一个字符。

我建议通过单引号保护字符串,因为它几乎可以保留所有内容:

grep '9\.0' #send those 4 characters to grep in a single argument
Run Code Online (Sandbox Code Playgroud)

Shell 逐字传递单引号字符串。

注意:唯一不能包含在单引号 shell 字符串中的是单引号(因为这会结束单引号)。要在单引号 shell 字符串中包含单引号,您需要首先结束单引号,立即添加一个转义的\'单引号(或双引号之间的一个:)"'",然后立即重新输入单引号以继续单引号字符串:例如让 shell 执行命令grep a'b,您可以编写参数,'a'\''b'以便 shell 发送a'b到 grep: so write: grep 'a'\''b',或 grep 'a'"'"'b'

如果你坚持不使用引用,你需要你的 shell 有一个\\让它发送\到 grep。

grep 9\\.0  # ie: a 9, a pair \\, a ., and a 0 , and the shell interprets the pair \\ into a literal \
Run Code Online (Sandbox Code Playgroud)

如果使用双引号:你需要考虑的是,壳牌将解释几件事情第一($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
Run Code Online (Sandbox Code Playgroud)

  • @dbates:另一件需要注意的事情:正则表达式有很多“风格”,每种风格也有不同的实现......有时你会使用 `s/first\(second\)thirdregexp/\1/ ` 只保留与“second”匹配的内容(作为正则表达式),有时您必须使用非反斜杠括号来做同样的事情。和maaany其他变化。我喜欢以下信息:http://www.regular-expressions.info/ 和一个很酷的工具 (http://www.regular-expressions.info/regexbuddy.html)“模拟 262 应用程序的正则表达式风格和编程语言” (2认同)

Hau*_*ing 9

把评论变成答案:

问题是这\是正则表达式和外壳的转义字符。\.与外壳相同'.'echoset -x帮助理解 shell 的作用:

> echo \.
.

> echo '\.'
\.

> echo \\.
\.


> set -x
> echo 9_00 | grep 9\.00
+ echo 9_00
+ grep 9.00
9_00
Run Code Online (Sandbox Code Playgroud)

因此,如果命令将看到,\那么它必须由引号或第二个保护\