您似乎已经从Unix & Linux 交叉发布了这个问题 - 我在这里给出的答案与我在那里所做的相同:
我将假设您发布的内容是一个示例,因为它不是有效的 XML。如果这个假设不成立,我的回答就不成立……但如果是这样的话,你真的需要用一份 XML 规范的卷起副本来打击给你 XML 的人,并要求他们'修复它。
但实际上 - awk 和正则表达式并不是适合这项工作的工具。 XML 解析器是。使用解析器,做你想做的事非常简单:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
#parse your file - this will error if it's invalid.
my $twig = XML::Twig -> new -> parsefile ( 'your_xml' );
#set output format. Optional.
$twig -> set_pretty_print('indented_a');
#iterate all the 'record' nodes off the root.
foreach my $record ( $twig -> get_xpath ( './record' ) ) {
#if - beneath this record - we have a node anywhere (that's what // means)
#with a tag of 'keyword' and content of 'SEARCH'
#print the whole record.
if ( $record -> get_xpath ( './/keyword[string()="SEARCH"]' ) ) {
$record -> print;
}
}
xpath 很像正则表达式——在某些方面——但它更像是一个目录路径。这意味着它是上下文感知的,并且可以处理 XML 结构。
在上面:./ 表示“低于当前节点”,所以:
$twig -> get_xpath ( './record' )
表示任何“顶级”<record> 标签。
但是.// 的意思是“在任何级别,低于当前节点”,所以它会递归地执行它。
$twig -> get_xpath ( './/search' )
将获得任何级别的任何<search> 节点。
方括号表示一个条件——要么是一个函数(例如text() 获取节点的文本),要么你可以使用一个属性。例如//category[@name] 会找到任何具有 name 属性的类别,//category[@name="xyz"] 会进一步过滤这些类别。
用于测试的 XML:
<XML>
<record category="xyz">
<person ssn="" e-i="E">
<title xsi:nil="true"/>
<position xsi:nil="true"/>
<details>
<names>
<first_name/>
<last_name></last_name>
</names>
<aliases>
<alias>CDP</alias>
</aliases>
<keywords>
<keyword xsi:nil="true"/>
<keyword>SEARCH</keyword>
</keywords>
<external_sources>
<uri>http://www.google.com</uri>
<detail>SEARCH is present in abc for xyz reason</detail>
</external_sources>
</details>
</person>
</record>
<record category="abc">
<person ssn="" e-i="F">
<title xsi:nil="true"/>
<position xsi:nil="true"/>
<details>
<names>
<first_name/>
<last_name></last_name>
</names>
<aliases>
<alias>CDP</alias>
</aliases>
<keywords>
<keyword xsi:nil="true"/>
<keyword>DONTSEARCH</keyword>
</keywords>
<external_sources>
<uri>http://www.google.com</uri>
<detail>SEARCH is not present in abc for xyz reason</detail>
</external_sources>
</details>
</person>
</record>
</XML>
输出:
<record category="xyz">
<person
e-i="E"
ssn="">
<title xsi:nil="true" />
<position xsi:nil="true" />
<details>
<names>
<first_name/>
<last_name></last_name>
</names>
<aliases>
<alias>CDP</alias>
</aliases>
<keywords>
<keyword xsi:nil="true" />
<keyword>SEARCH</keyword>
</keywords>
<external_sources>
<uri>http://www.google.com</uri>
<detail>SEARCH is present in abc for xyz reason</detail>
</external_sources>
</details>
</person>
</record>
注意 - 以上只是将记录打印到 STDOUT。这实际上......在我看来,这不是一个好主意。尤其是因为 - 它不打印 XML 结构,因此如果您有多个记录(没有“根”节点),它实际上不是“有效”XML。
所以我会改为 - 完全按照您的要求完成:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $twig = XML::Twig -> new -> parsefile ('your_file.xml');
$twig -> set_pretty_print('indented_a');
foreach my $record ( $twig -> get_xpath ( './record' ) ) {
if ( not $record -> findnodes ( './/keyword[string()="SEARCH"]' ) ) {
$record -> delete;
}
}
open ( my $output, '>', "output.txt" ) or die $!;
print {$output} $twig -> sprint;
close ( $output );
这相反 - 反转逻辑,并删除(从内存中已解析的数据结构中)您不想要的记录,并将整个新结构(包括 XML 标头)打印到新的名为“output.txt”的文件。