【问题标题】:Translate PCRE pattern to POSIX将 PCRE 模式转换为 POSIX
【发布时间】:2016-02-28 18:57:11
【问题描述】:

我有以下 pcre 可以正常工作:

/[c,f]=("(?:[a-z A-Z 0-9]|-|_|\/)+\.(?:js|html)")/g

它从输入中生成所需的输出“foo.js”和“bar.html”

<script src="foo.js"...
<link rel="import" href="bar.html"...

问题是,OS X 版本的 grep 似乎没有像 -o 这样的选项来仅打印捕获的组(根据另一个 SO 问题,这显然适用于 linux)。由于这将是 makefile 的一部分,因此我需要一个可以在任何 *nix 平台上运行的版本。

我尝试了 sed 但以下

s/[c,f]=("(?:[[:alphanum:]]|-|_|\/)+\.(?:js|html)")/\1/pg

引发错误:“重复运算符的操作数无效”。我试过把它修整下来,不包括文件路径分隔符,我似乎无法破解它。任何帮助将我的 pcre 翻译成我几乎可以保证在符合 POSIX 的(即使是非官方的)平台上拥有的东西?

附:我知道我编写的正则表达式中固有的潜在故障模式,它只会用于具有相当特定格式的非常特定的文件。

【问题讨论】:

  • 没有 "universal" 正则表达式,FTFY ;-)
  • @LucasTrzesniewski 没有,但有通用的 POSIX 实用程序,如 grep 和 sed。问题是 grep 标志显然是平台相关的(沿着 BSD/gnu 行),我不知道如何在 sed 中做到这一点。我只是不能指望 pcregrep 或 gnu grep。不过,您的编辑肯定是有说服力的。

标签: regex sed


【解决方案1】:

POSIX 定义了两种风格正则表达式

  • BREs (Basic Regular Expressions) - 具有较少功能且需要 \ 转义某些元字符的旧版本,特别是 \(\)\{\}no 支持重复符号\+(用\{1,\} 模拟)和\?(用\{0,1\} 模拟),并且支持\|(交替;无法模拟)。

  • EREs (Extended Regular Expressions) - 更现代的风格,但是缺少正则表达式内部的反向引用(与捕获组相同);还有支持词边界断言(例如\&lt;)和支持捕获组.

POSIX 还规定哪些实用程序支持哪种风格:哪些支持 BRE,哪些支持 ERE,哪些可选支持任一,以及哪些独家仅支持 BRE,或仅支持 ERE;特别是:

  • grep 默认使用 BRE,但可以使用 -E 启用 ERE
  • sed,遗憾的是,支持 BRE
    • 不过,GNU 和 BSD sed - 作为一个非标准扩展 - 确实 支持带有 -E 开关的 ERE(GNU @ 更广为人知的别名) 987654341@ 是-r,但也支持-E
  • awk 支持 ERE

此外,Linux 和 BSD/OSX 上的正则表达式库都实现了 POSIX ERE 语法的扩展 - 遗憾的是,这些扩展部分不兼容(例如单词边界断言的语法)。

至于您的特定正则表达式

它使用捕获组的语法(?:...);但是,捕获组在grep 的上下文中毫无意义,因为grep 不提供替换 功能。

如果我们去掉这个方面,我们会得到:

[c,f]=("([a-z A-Z 0-9]|-|_|\/)+\.(js|html)") 

现在这是一个有效的 POSIX ERE(可以简化 - 请参阅 Benjamin W's helpful answer)。
但是,由于它是一个扩展 RE,如果您想严格遵守 POSIX,则使用sed 不是一个选项。

因为 GNU 和 BSD/OSX sed 都恰好实现了 -E 来支持 ERE,如果这些平台是唯一需要支持的 - 请参阅 anubhava's answer

同样,GNU 和 BSD/OSX grep 都恰好实现了 非标准 -o 选项(与您在问题中所说的不同),所以,再次,如果这些平台是您唯一需要支持的,您可以使用:

$ grep -Eo '[c,f]=("([a-z A-Z 0-9]|-|_|\/)+\.(js|html)")' file | cut -c 3-
c="foo.js"
f="bar.html"

(请注意,只有 GNU grep 支持 -P 来启用 PCRE,这只是解决方案(注意 \K,它会丢弃迄今为止匹配的所有内容):

$ grep -Po '[c,f]=\K("([a-z A-Z 0-9]|-|_|\/)+\.(js|html)")' file

)

如果您真的想要严格符合 POSIX 的解决方案,您可以使用awk

$ awk -F\" '/[c,f]=("([a-z A-Z 0-9]|-|_|\/)+\.(js|html)")/ { print "\"" $2 "\"" }' file

【讨论】:

  • 我只需要担心 BSD、OS X 和 linux,所以其他答案可以满足我的需求,但无论如何感谢您提供更便携的解决方案。
  • @JaredSmith:我的荣幸;那么你应该接受另一个答案。
【解决方案2】:

在 OSX 上,以下 sed 应该适用于您给定的输入:

sed -E 's~.*[cf]=("[ a-zA-Z0-9_/-]+\.(js|html)").*~\1~' file

"foo.js"
"bar.html"

RegEx Demo

【讨论】:

  • 按预期工作。解释这将是如何工作的。看起来您使用波浪号而不是斜线作为分隔符,但我不确定我是否理解这与我原来的正则表达式之间的区别。
  • ++;它也适用于 GNU sed(GNU sed 接受 -E 作为 -r 的别名)。
  • @JaredSmith:实际上它与您所讨论的 PCRE 正则表达式相同。我只是对其进行了一些重构,以删除第一个 [...] 中的冗余逗号,将 -|_|\/ 转换为 [_/-] 并删除了非捕获组,因为 ERE 或 BRE 不支持该组。
  • 换句话说,您也可以使用:sed -E 's/.*[c,f]=("([a-z A-Z 0-9]|-|_|\/)+\.(js|html)").*/\1/' file,只需删除 (?:...) 作为非捕获组,因为 sed 不支持该组
【解决方案3】:

spec for POSIX sed 指出仅支持basic regular expressions (BRE),因此不支持+|;非捕获组甚至不在 extended regular expressions (ERE) 的规范中。

谢天谢地,GNU sed 和 BSD sed 都支持 ERE,所以我们可以使用交替和 + 量词。

几点:

  • 您真的想要第一个括号表达式中的逗号吗?我怀疑它可能只是[cf]
  • 表达式

    (?:[a-z A-Z 0-9]|-|_|\/)+
    

    可以简化为单括号表达式,

    [a-zA-Z0-9_\/ -]+
    

    只需要一个空格。您还可以使用 POSIX 字符类:[[:alnum:]]_/ -]+。不确定您的 [:al<b>pha</b>num:] 是否被绊倒了。

  • 对于引号之间的整个表达式,我只使用“引号之间的东西,以.js.html 结尾,前面是非引号”的表达式:

    "[^"]+\.(js|html)"
    
  • 要模拟 grep -o 的行为,您还必须将表达式前后的所有内容与正则表达式开头和结尾处的 .* 匹配。

总而言之,我想说对于使用 ERE 的 sed(GNU sed 的-r 选项,BSD sed 的 -E 选项),这应该可以工作:

sed -rn 's/.*[cf]=("[^"]+\.(js|html)").*/\1/p' infile

或者,仅使用 BRE(由于交替需要两个命令):

sed -n 's/.*[cf]=\("[^"][^"]*\.js"\).*/\1/p;s/.*[cf]=\("[^"][^"]*\.html"\).*/\1/p' infile

注意 BRE 如何使用 [abc][abc]* 而不是 [abc]+ 来模拟 + 量词。

这种方法的局限性在于,如果同一行上有多个匹配项,则只会打印第一个匹配项,因为s/// 命令会删除我们提取部分之前和之后的所有内容。

【讨论】:

  • 感谢您的分解。不,每行不会有多个匹配项,问题是我正在编写的makefile中有一个条件,即要么对文件进行分类,要么去掉正则表达式匹配项,文件结构是已知的。至于 alphanum 的问题,我是从一些 90 年代中期清楚写成的文档网站中提取出来的。
  • ++;请注意 GNU sed 接受 -E(代替 -r),即使手册页没有这样说。
  • @mklement0 确实如此!有趣的。使其符合grep -E
  • 是的,但不幸的是,使用sed -E,您确实必须将自己限制在 POSIX ERE 功能(以及许多其他 POSIX 规定的sed 行为 - 请参阅 @ 987654324@ 我的血淋淋的细节)使给定的命令在 Linux 和 BSD/OSX 上都能工作;使用grep -E,非标准 ERE 扩展有更多重叠。
猜你喜欢
  • 2011-07-14
  • 1970-01-01
  • 1970-01-01
  • 2011-01-22
  • 1970-01-01
  • 2020-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多