如何匹配单个行字符串上最后一次出现的模式

时间:2017-04-22 23:28:25

标签: regex linux bash grep

我正在使用此命令行从包含各种其他标签,链接等的html文件中获取特定行:

cat index.html | grep -m1 -oE '<a href="(.*?)" rel="sample"[\S\s]*.*</dd>'

输出我想要的行:

<a href="http://example.com/something/one/" rel="sample" >Foo</a> <a href="http://example.com/something/two/" rel="sample" >Bar</a></dd>

但我想只捕获something/two最后网址的路径),考虑到:

  • 事先不知道网址(它是处理多个html文件的脚本)
  • 该行有时只能包含1个URL,例如

    <a href="http://example.com/something/one/" rel="sample" >Foo</a></dd>
    

    在这种情况下,我希望只获得something/one,因为在这种情况下它是最后一个

我该怎么做?

3 个答案:

答案 0 :(得分:2)

添加

| grep -o 'href="[^"]*' | tail -n1

第一部分仅提取href s,第二部分仅保留最后一行。

如果您只想提取路径,可以使用cut并将分隔符设置为/,并从第四列开始提取所有内容:

| grep -o 'href="[^"]*' | tail -n1 | cut -f4- -d/

,因为

href="http://example.com/something/two/
1          23            4         5

答案 1 :(得分:1)

如果你可以使用perl,那么在正则表达式中捕获会使这更容易。

 perl -ne 'm(.*<a href="[^:]+://[^/]*/(.*?)" rel="sample".*</dd>) and print "$1\n";'

正则表达式基本上与grep一样。我使用了m()代替//来避免在正则表达式中转义/

最初的.*会贪婪地捕捉到行尾的所有内容。如果一行上有多个链接,它将捕获除最后一个之外的所有链接。这也适用于grep,但它会导致grep -o输出行的开头,因为它现在匹配正则表达式。

这与捕获括号无关,因为只捕获并打印(.*?)内的部分。

它将以与grep相同的方式使用。

cat index.html | perl -ne 'm(.*<a href="[^:]+://[^/]*/(.*?)" rel="sample".*</dd>) and print "$1\n";'

or

perl -ne 'm(.*<a href="[^:]+://[^/]*/(.*?)" rel="sample".*</dd>) and print "$1\n";' index.html

答案 2 :(得分:1)

在Linux上,GNU grep的{​​{1}}选项可以提供简洁的解决方案:

-P

$ grep -oP '.*<a href="http://.+?/\K[^"]+(?=/"\s*rel="sample".*</dd>$)' index.html something/two 仅输出匹配的每一行的匹配部分

-o激活对PRCEs (Perl-compatible Regular Expressions)的支持,{{3}}支持高级正则表达式构造,例如非贪婪匹配(-P),删除到目前为止匹配的所有内容(*?),以及预见断言(\K)。

  • (?=...\K的组合允许将正则表达式的匹配部分约束到感兴趣的子表达式
    请注意, no (?=...)实现支持捕获组,但由于grep启用的功能,上述内容是提取单个的模拟捕获组值。

至于您尝试的内容

  • -P将匹配的的数量限制为1,但如果-m1也存在,则多个匹配 on <1> 仍然全部打印。

    • 此外,虽然您可以将-o用于优先级,但这并不构成(...)中的捕获组,因为它有不支持在grep 中提取捕获组值。
  • 即使grep支持扩展正则表达式支持,非贪婪匹配(-E)等高级构造也不支持