【问题标题】:Matching word boundary with Bash regex使用 Bash 正则表达式匹配单词边界
【发布时间】:2014-12-15 01:53:27
【问题描述】:

我想在 bash 中匹配以下表达式:

^.*(\b((720p)|(1080p)|(((br)|(hd)|(bd)|(web)|(dvd))rip)|((x|h)264)|(DVDscr)|(xvid)|(hdtv)|(ac3)|(s[0-9]{2}e[0-9]{2})|(avi)|(mp4)|(mkv)|(eztv)|(YIFY))\b).*$

我真的只想知道测试的字符串中的一个单词是否是这个正则表达式中描述的单词之一(720p1080pbrrip,...)。而且边界这个词似乎有问题。

我使用的测试是[[ $name =~ $re ]] && echo "yes",其中$name 是任何字符串,$re 是我的正则表达式。

我错过了什么?

【问题讨论】:

  • 单引号——如re='yadda yadda yadda'——不会破坏你的反斜杠。
  • 我不理解反对意见:第一行中接受的答案明确说明了为什么需要回答。 \b 是 PCRE 扩展;它在 ERE 中不可用,bash 的 [[ ]] 语法中的 =~ 运算符使用它。
  • 补充一点,Bash-3.0Bash-3.1 肯定使用了 PCRE 语法,可以在 Bash-4.0 和更高版本中使用 shopt -s compat31 启用。
  • 忽略我之前的评论。看起来接受的答案让我感到困惑。用答案修复它。
  • @Samveen,嗯?让您失望的不是 PCRE 语法,而是 ERE 的供应商扩展添加了源自 PCRE 的(单一的、特定的)功能。 Bash 使用本地操作系统的 libc,因此它隐式选择了您的操作系统供应商选择提供的所有扩展。

标签: regex bash word-boundary


【解决方案1】:

\b 是 PCRE 扩展;它在 POSIX ERE(扩展正则表达式)中不可用,这是 bash 的 [[ ]] 中的 =~ 运算符将遵循的最小可能的语法集。 (单个操作系统可能具有扩展此语法的 libc;在这种情况下,这些扩展将在此类操作系统上可用,但并非在支持 bash 的所有平台上)。

作为基线,\b 扩展实际上并没有很强的表达能力——您可以编写任何 PCRE 将其用作等效的 ERE。不过,更好的是退后一步,质疑基本假设:当你说“单词边界”时,你的真正意思是什么?如果您关心的是,如果它以空格或字符串的开头或结尾开始和结束,那么您根本不需要 \b 运算符:

(^|[[:space:]])((720p)|(1080p)|(((br)|(hd)|(bd)|(web)|(dvd))rip)|((x|h)264)|(DVDscr)|(xvid)|(hdtv)|(ac3)|(s[0-9]{2}e[0-9]{2})|(avi)|(mp4)|(mkv)|(eztv)|(YIFY))($|[[:space:]])

请注意,我去掉了最初的 ^.* 和结尾的 .*$,因为这些构造在进行其他未锚定的匹配时会自我否定; .* 使紧接在它之前的 ^ 变得毫无意义,同样,在最后的 $ 之前的 .*


现在,如果你想要一个 exact 等同于 \b 时放置在序列开头的单词字符之前,那么我们会得到更像:

(^|[^a-zA-Z0-9_])

...同样,当紧跟在序列末尾的单词字符之后时:

($|[^a-zA-Z0-9_])

这两种情况都有些退化——在其他情况下,在 ERE 中模拟 \b 的行为可能会更复杂——但它们是您的问题出现的唯一情况。

请注意,\b 的某些实现对非 ASCII 字符集有更好的支持,因此用[^[:alnum:]_] 而不是[^a-zA-Z0-9_] 更好地描述,但这里没有明确定义您将采用哪种实现来自或比较。

【讨论】:

  • 这就是我详细说明我的问题的原因,我认为还有另一种方法。但是[:space:] 有点太严格了,你应该改用[^a-z0-9]
  • 使用(^|[^a-zA-Z0-9_])($|[^a-zA-Z0-9_]) 吸收字符。这里可能没问题,但是例如非 GNU sed 无法将 echo foo foo foo foo |sed 's/\bfoo\b/bar/g'echo foo foo foo foo |sed -E 's/(^|[^[:alnum:]_])foo($|[^[:alnum:]_])/\1bar\2/g' 等同起来,因为在 foos 之间只有一次空格;你最终会得到bar foo bar foo。更新:Charles Duffy 有 a solution to this issue 使用 GNU 正则表达式,因此 \b 可用。
【解决方案2】:

接受的答案是错误的可能在两个小问题上是错误的:

  • 据我所知,\b 和 '\'(字边界匹配)不是 PCRE 创新。再说一次,我无法追踪在 RE 引擎中引入了词边界匹配,所以它也可能是 Perl。
  • 正如答案中正确说明的那样,POSIX ERE 不支持单词边界匹配。但是,所有现代正则表达式引擎都提供单词边界匹配作为基本 RE 的一部分,而不仅仅是 ERE:您只需要找到语法即可。

也就是说,这个答案对于BashLinux 构建非常具体(带有最后的MacOSX 特定部分,可能适用于所有 BSD衍生产品)。

  • 根据定义,GNU Regular Expressions (RE) 支持 \b\<|\> 作为单词边界(grep 语法)。 它不是 Perl 兼容的正则表达式扩展,AFAIK。 [1]

  • Bash3.0 以来一直支持 GNU 扩展 REgrep -E 语法)。 [2]

  • 因此对于Bash >= 3.0 的所有版本,[[ " h " =~ '\bh\b' ]] && echo yes || echo no 应该给我yes。它没有(请参阅下一点)。

  • Bash 版本中3.03.1[[ " h " =~ '\bh\b' ]] && echo yes || echo no 会给我yes。请注意,模式本身是 =~ 运算符的 right hand side (RHS) 参数。 [2]

  • Bash-3.2 更改了匹配运算符 =~ 的引用规则。 [2]

  • 由于Bash-3.2,理想情况下模式应存储在变量中,并且该变量应作为RHS 参数提供给=~ 运算符:pat='\bh\b' ; [[ " h " =~ $pat ]] && echo yes || echo no。原因是引用规则发生了变化,因此如果在引号内提供模式(''""),则模式将被解释为字符串而不是正则表达式。 [2]

最后,你的模式是正确的,这只是一个奇怪的引用问题:

[samveen@ankhmorpork ~]# echo $BASH_VERSION
4.2.46(1)-release
[samveen@ankhmorpork ~]# re='^.*(\b((720p)|(1080p)|(((br)|(hd)|(bd)|(web)|(dvd))rip)|((x|h)264)|(DVDscr)|(xvid)|(hdtv)|(ac3)|(s[0-9]{2}e[0-9]{2})|(avi)|(mp4)|(mkv)|(eztv)|(YIFY))\b).*$'
[samveen@ankhmorpork ~]# for i in 720p 1080p brrip; do
>      [[ $i =~ $re ]] && echo yes for $i || echo no for $i
> done
yes for 720p
yes for 1080p
yes for brrip

此外,对于MacOSX 上的Bash,边界匹配从\b 更改为'[[:<:]](单词开头)和[[:>:]](单词结尾)[3]:

SamveensMBP:~ samveen$ echo $BASH_VERSION
3.2.57(1)-release
SamveensMBP:~ samveen$ re='^.*([[:<:]]((720p)|(1080p)|(((br)|(hd)|(bd)|(web)|(dvd))rip)|((x|h)264)|(DVDscr)|(xvid)|(hdtv)|(ac3)|(s[0-9]{2}e[0-9]{2})|(avi)|(mp4)|(mkv)|(eztv)|(YIFY))[[:>:]]).*$'
SamveensMBP:~ samveen$ for i in 720p 1080p brrip; do
>     [[ $i =~ $re ]] && echo yes for $i || echo no for $i
> done
yes for 720p
yes for 1080p
yes for brrip

参考资料:

[1]GNU grep Manual: Regex section

[2]The Bash FAQ, by it's Author

[3]MacOSX manpage for re_format

【讨论】:

  • 如果您想谈论 POSIX ERE(标准,而不是该标准的 GNU 实现)支持的内容,您最好链接到标准本身。
  • 该标准的全部内容位于pubs.opengroup.org/onlinepubs/009696899/basedefs/…——实际上没有指定\b。 ERE 的 GNU 实现包含扩展对任何人来说都不足为奇,但它们是扩展,并且在操作系统上不可用(对于使用 C 库匹配功能的工具,如 bash)不使用 glibc。 (相应地,GNU grep 手册几乎不合适——glibc 和 GNU grep 是否尊重相同的语法取决于实现)。
  • ...这个答案可以很容易地通过修改措辞来特别提到 ERE 的 GNU 实现来纠正,但就目前而言,有一个强有力的论据表明它实际上并不准确。至于我自己的答案,我已对其进行了修改,以专门参考 POSIX ERE 标准,而不是任何特定供应商的 ERE 实现;希望这足以让您撤回您的不准确声明,以及推定相关的反对票。
  • 也就是说:在您的回答中声称 bash 将始终支持“GNU ERE”是完全错误的,您链接到的常见问题解答没有做出这样的声明。引用:The '[[' command can now perform extended regular expression (egrep-like) matching——不能保证它会是 GNU 扩展语法。
  • ...顺便说一句,一个这样的平台很容易获得:MacOS。如果您想亲自证明我的主张,请在 Linux 和 Mac 上相同版本的 bash 上运行 pat='\bh\b' ; [[ " h " =~ $pat ]] &amp;&amp; echo yes || echo no
猜你喜欢
  • 2022-07-06
  • 2012-08-02
  • 1970-01-01
  • 2019-05-17
  • 1970-01-01
  • 1970-01-01
  • 2010-12-05
  • 2011-01-26
  • 1970-01-01
相关资源
最近更新 更多