我在XML文件中有记录,如下所示。我需要搜索<keyword>SEARCH</keyword>
以及是否存在
然后我需要获取整个记录并写入另一个文件。(从<record>
开始到</record>
)
下面是我的awk代码,它是内部循环。 $1
保存每条记录的逐行值。
if(index($1,"SEARCH")>0)
{
print $1>> "output.txt"
}
这个逻辑有两个问题,
output.txt
个文件,只有<keyword>SEARCH</keyword>
个元素,而不是整个记录(从<record>
到</record>
)<detail>
标记中。此代码甚至会将该标记写入output.txt
XML文件:
<record category="xyz">
<person ssn="" e-i="E">
<title xsi:nil="true"/>
<position xsi:nil="true"/>
<names>
<first_name/>
<last_name></last_name>
<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>
</record>
<record category="abc">
<person ssn="" e-i="F">
<title xsi:nil="true"/>
<position xsi:nil="true"/>
<names>
<first_name/>
<last_name></last_name>
<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>
</record>
答案 0 :(得分:1)
$ cat x.awk
/<record / { i=1 }
i { a[i++]=$0 }
/<\/record>/ {
if (found) {
for (i=1; i<=length(a); ++i) print a[i] > "output.txt"
}
i=0;
found=0
}
/<keyword>SEARCH<\/keyword>/ { found=1 }
$ awk -f x.awk x.xml
$ cat output.txt
<record category="xyz">
<person ssn="" e-i="E">
<title xsi:nil="true"/>
<position xsi:nil="true"/>
<names>
<first_name/>
<last_name></last_name>
<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>
</record>
答案 1 :(得分:1)
你似乎已经从Unix & Linux交叉发布了这个问题 - 我在这里给出了同样的答案:
我将假设您发布的内容是一个示例,因为它不是有效的XML。如果这个假设不有效,我的回答并不成立......但如果是这样的话,你真的需要找到那个用XML规范的卷起副本给你XML的人,并要求他们解决问题&#39;。
但实际上 - 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结构。
在上面:./
表示在当前节点&#39;下方。这样:
$twig -> get_xpath ( './record' )
意味着任何顶级&#39; <record>
个标签。
但.//
表示&#34;在任何级别,低于当前节点&#34;所以它会以递归方式进行。
$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结构,因此它实际上并不是有效的&#39;如果你有多个记录(那里没有&#34; root&#34;节点),那就是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头)一个名为&#34; output.txt&#34;。
的新文件答案 2 :(得分:1)
将GNU awk用于多字符RS:
$ awk -v RS='</record>\n' '{ORS=RT} /<keyword>SEARCH<\/keyword>/' file
<record category="xyz">
<person ssn="" e-i="E">
<title xsi:nil="true"/>
<position xsi:nil="true"/>
<names>
<first_name/>
<last_name></last_name>
<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>
</record>
如果您需要搜索多个关键字中的任何一个,那么只需将它们列为:
$ awk -v RS='</record>\n' '{ORS=RT} /<keyword>(SEARCH1|SEARCH2|SEARCH3)<\/keyword>/' file