我有一些文本文件部分包含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