将grep正则expression式多行search的匹配结果限制为一个

我有一些文本文件部分包含XML数据。 例如:

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

我需要做多行search,只提取特定标签内的数据。 我尝试了一些在这里find的解决scheme,并且我在perl-regexp模式下使用了grep的最佳结果:

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

但有时文件可能包含两个或多个相同模式的相同模块。 我怎样才能改变这个正则expression式来限制grep输出第一次出现? -m参数在perl正则expression式模式下不起作用。

ps:其他工作解决scheme是可以的,但是使用XML专用工具不是一个选项。 文件实际上是使用strings实用程序渗透的内存转储,它们仅包含其他数据中的SOAP事务的片段。 在这种情况下,我必须使用正则expression式。

这是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>的行在它被删除之后被打印,然后sed退出。 这是如何提取第一场比赛。

  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>的行的其余部分将仅在非空(第一版本)或包含多个空格(第二版本)的情况下打印。 这可以防止(可能)在提取的文本末尾多余的换行符。

你需要使用\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. 

要么

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