将grep regex多行搜索的匹配结果限制为一个

时间:2014-12-13 01:15:30

标签: regex linux command-line grep pcre

我有一些部分包含XML数据的文本文件。 E.g:

<soap:Envelope xmlns:soap="..."><soap:Body><Data><SpecificTag>Some
multiline data
that I need to
extract.
</SpecificTag></Data></soap:Body></soap:Envelope>

我需要进行多行搜索并仅提取特定标记内的数据。我在这里尝试了一些解决方案,以及在perl-regexp模式下使用grep获得的最佳结果:

grep -Pzo '(?s)<SpecificTag>\K.*?(?=</SpecificTag>)' filename

但有时文件可能包含两个或多个具有匹配模式的相同块。如何更改此正则表达式以将grep输出限制为第一次出现?在perl正则表达式模式下,-m参数不起作用。

p.s。:其他工作解决方案没问题,但使用特定于XML的工具不是一种选择。文件实际上是使用strings实用程序渗透的内存转储,它们仅包含SOAP事务的片段以及其他数据。在这种情况下我必须使用正则表达式。

2 个答案:

答案 0 :(得分:2)

您需要使用\A锚点来匹配第一行的开头。

grep -Pzo '(?s)\A.*?<SpecificTag>\K.*?(?=</SpecificTag>)' file

示例:

$ cat file
<soap:Envelope xmlns:soap="..."><soap:Body><Data><SpecificTag>Some
multiline first data
that I need to
extract.
</SpecificTag></Data></soap:Body></soap:Envelope>
<SpecificTag>Some
multiline second data
that I need to
extract.

$ grep -Pzo '(?s)\A.*?<SpecificTag>\K.*?(?=</SpecificTag>)' file
Some
multiline first data
that I need to
extract.

OR

grep -Pzo '(?s)\A.*?<SpecificTag>\K(?:(?!</?SpecificTag>).)*(?=</SpecificTag>)' file

答案 1 :(得分:0)

这里有一些关于sed的内容:

/<SpecificTag>/,/<\/SpecificTag>/ {
  /<SpecificTag>/ {
    s/.*<SpecificTag>//
  }
  /<\/SpecificTag>/ {
    s/<\/SpecificTag>.*//
    p
    q
  }
  p
}

将其放入文件中,例如foo.sed,然后使用sed -n -f foo.sed filename.xml

其工作方式如下:

/<SpecificTag>/,/<\/SpecificTag>/ {

表示所有这些只发生在<SpecificTag></SpecificTag>之间的行。

  /<SpecificTag>/ {
    s/.*<SpecificTag>//
  }

表示在该约束中,包含<SpecificTag>的行包含它,并删除它之前的所有内容。

  /<\/SpecificTag>/ {
    s/<\/SpecificTag>.*//
    p
    q
  }

表示包含</SpecificTag>的行包含它,删除后的所有内容都会被打印,然后退出。这就是提取第一个匹配的方式。

  p
}

表示打印第一个约束(标记之间)中的所有其他行。这包括替换后第一行的其余部分。

如果您希望在一个长命令中使用它:

sed -n -e '/<SpecificTag>/,/<\/SpecificTag>/ { /<SpecificTag>/ { s/.*<SpecificTag>// }; /<\/SpecificTag>/ { s/<\/SpecificTag>.*//; p; q }; p }' filename.xml

...但当然这让人很难看到发生的事情,并且sed脚本已经非常难以阅读。

附录:您可能想要考虑的一个补充是

  /<\/SpecificTag>/ {
    s/<\/SpecificTag>.*//
    p
    q
  }

进入

  /<\/SpecificTag>/ {
    s/<\/SpecificTag>.*//
    /^$/ !p
    q
  }

或者甚至可能

    /^ *$/ !p

...在这种情况下,包含</SpecificTag>的行的其余部分只有在它不为空(第一个版本)或包含多个空格(第二个版本)时才会被打印。这可以防止(可能)在提取的文本末尾出现多余的换行符。