正则表达式:'\<'与'\b'的区别

4

目前正在准备RHCSA认证,并学习正则表达式\b\<有什么区别?

它们似乎做的事情几乎完全相同:匹配反斜杠之间的字符串。
例如:

[root@RHEL8DEV etc]# grep '\<root\>' * 2>/dev/null  | wc
    105     327    3658

[root@RHEL8DEV etc]# grep '\broot\b' * 2>/dev/null  | wc
    105     327    3658

即使在阅读了 gnu.org 之后,我仍然摸不着头脑。


使用 \b

  • \b 匹配一个空字符串,但只能在单词的开头或结尾出现。所以,\bfoo\b 只匹配作为一个独立单词的任何一次出现的 foo\bballs?\b 匹配作为一个独立单词的 ballballs。无论旁边的文本是什么,\b 都匹配到缓冲区的开头或结尾。

使用 \<\>

  • \< 匹配一个空字符串,但只能在一个单词的开头出现。只有当一个单词的字符跟在它后面时,\< 才会在缓冲区的开头处匹配。
  • \> 匹配一个空字符串,但只能在单词的结尾出现。只有当内容以一个单词-构成字符结束时,\> 才会在缓冲区的结尾处匹配。

感谢您抽出时间阅读这篇文章。


1
grep 不是 bash 的一部分;它可以在没有安装 shell 的情况下运行。考虑使用 unix 标签,并指定特定的操作系统供应商和版本(或者如果它来自第三方如 GNU 项目,则指定 grep 版本)。 - Charles Duffy
1
每个工具也会指定它们理解的正则表达式方言。\b\<都没有通用含义。 - chepner
还有第三个选项,[[:<:]][[:>:]]。它在macOS和可能的BSD(我检查了OpenBSD)中支持grepsed。可悲的是,GNU不支持它。 - imgx64
2个回答

4
您特定版本的 grep 的手册页面才能揭示它们是否完全相等,两者都不是完全可移植的。
传统上,在某些版本的 egrep 中,\< 只能匹配左边的单词边界,而 \> 则匹配右边的单词边界。(但是,例如 Procmail 走了捷径,实际上将两个标记定义为相同。) \b 是Perl等语言中较新的构造,方向中立,即它在单词字符序列的左侧或右侧的单词边界上都成立。

最后一段可能有歧义。\b 本质上是 (?:\<|\>) - Adam Katz
当然,(?:...) 也是 Perl 的扩展。我不认为这有歧义,但如果有的话,也许你的评论可以帮助某些人澄清它。 - tripleee
是的,我应该说“(<|>)”假定扩展正则表达式,同时指出PCRE不支持它(参见我的答案)。 我所指出的歧义是您使用“and”而不是“or”。 - Adam Katz
谢谢反馈,已更改为使用“或”。 - tripleee

2
我个人发现`\b`比`\<`和`\>`更受广泛支持。我遇到的唯一例外是vim和BSD sed支持`\<`和`\>`而不支持`\b`。
至于它们的定义:在PCRE中,它基本上是这样的: 这些链接指向 Regex101 对这些正则表达式的解释。请注意,该网站支持的四个引擎都不理解 \<\> 应该执行什么操作。
由于 PCRE 明确禁止非字母数字转义的特殊含义,\< 表示“文字开角括号”,因此 (?:\<|\>) 表示 [<>] 而不是 \b。标准扩展正则表达式没有这种明确的禁止规定,尽管它们也没有实现任何这样的特殊含义(诸如 \<\> 的项目是非标准扩展)。
还要注意,在字符类内部,情况有所不同。在大多数正则表达式解释器中,[\b] 表示“文字退格符”,等同于 [\010][\x08](或 \010\x08)。将零宽度项放入字符类中毫无意义。
以下是使用 GNU grep 进行比较的示例,该工具接受两种格式:
$ echo yes |grep '\<yes'
yes
$ echo yes |grep '\byes'
yes
$ echo yes |grep '\>yes'
# (no output here means it failed)
$ 

在这里,您可以看到方向性对于\<\>很重要,但对于\b则不重要。
各种支持测试,仅命令行(截至2019年11月25日的Debian Testing或注明的FreeBSD 11.2):
$ echo y |grep '\<y'       # GNU grep w/ BRE, Basic Regular Expression
y
$ echo y |grep -E '\<y'    # GNU grep w/ ERE, Extended Regular Expression
y
$ echo y |grep -P '\<y'    # GNU grep w/ libpcre, Perl-Compatible Regular Expression
$ echo y |perl -ne 'print if /\<y/'  # perl proper
$ echo y |sed '/\<y/!d'    # GNU sed with BRE
y
$ echo y |sed -r '/\<y/!d' # GNU sed with ERE
y
$ echo y |sed '/\<y/!d'    # BSD sed with BRE (FreeBSD 11.2)
y
$ echo y |sed -E '/\<y/!d' # BSD sed with ERE (FreeBSD 11.2)
y
$ echo y |gawk '/\<y/'     # GNU awk
y
$ echo y |mawk '/\<y/'     # More POSIX-aligned
$ 

# python test (result printed as an array, in this case empty for no matches)
$ echo y |python -c 'import re,sys; print re.findall(r"\<y", sys.stdin.read())'
[]

“grep -P”(它使用libpcre,并非总是编译到grep中)不匹配,因为PCRE无法将“\<”识别为除了文字字符“<”之外的任何内容。
$ echo y |grep '\by'       # GNU grep w/ BRE, Basic regex
y
$ echo y |grep -E '\by'    # GNU grep w/ ERE, Extended regex
y
$ echo y |grep -P '\by'    # GNU grep w/ libpcre, Perl-compatible regex
y
$ echo y |perl -ne 'print if /\by/'  # perl proper 
y
$ echo y |sed '/\by/!d'    # GNU sed with BRE
y
$ echo y |sed -r '/\by/!d' # GNU sed with ERE
y
$ echo y |sed '/\by/!d'    # BSD sed with BRE (FreeBSD 11.2)
$ echo y |sed -E '/\by/!d' # BSD sed with ERE (FreeBSD 11.2)
$ echo y |gawk '/\by/'     # GNU awk
$ echo y |mawk '/\by/'     # POSIX-ish awk
$ 

# python test
$ echo y |python -c 'import re,sys; print re.findall(r"\by", sys.stdin.read())'
['y']

请注意,BSD的sed接受\<但不接受\b,而GNU的sed两者都接受。

我在macOS 10.15 Catalina上进行了测试:grep支持\b\</\>,而sedawk都不支持。 - imgx64

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接