2

我想在 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).*$

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

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

我错过了什么?

4

2 回答 2

5

\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:]])

请注意,我取出了 initial^.*和 ending .*$,因为在进行其他未锚定的匹配时,这些构造是自否定的;the.*使得^紧接在它之前的 the 没有意义,同样地,.*就在 final 之前$


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

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

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

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

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

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

于 2014-12-15T02:18:23.127 回答
2

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

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

也就是说,这个答案非常特定Linux构建Bash(最后一个MacOSX特定部分,也可能适用于所有 BSD 衍生产品)。

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

  • Bash自. RE_ grep -E_ 3.0[2]

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

  • Bash版本3.0通过3.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

此外,对于Bashon MacOSX,边界匹配从(单词开头)和(单词结尾)更改\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 手册:正则表达式部分

[2] Bash 常见问题解答,作者

[3]用于 re_format 的 MacOSX 手册页

于 2016-07-13T05:44:50.390 回答