我有一个包含此XML数据的文件:
<?xml version="1.0" encoding="utf-8"?>
<root>
<item>
<tag1>some text</tag1>
<tag2><![CDATA[http://url1.com]]></tag2>
<tag3 />
<tag4>not empty node</tag4>
</item>
<item>
<tag1>some other text</tag1>
<tag2><![CDATA[http://www.url.com]]></tag2>
<tag3 />
<tag4 />
</item>
</root>
(里面还有更多的XML)
我正在尝试编写一个Bash脚本来删除一些XML。也就是说,我想删除每个具有空<item>
子元素的<tag4>
元素。
因此,我想查找<item>
,然后找到<tag4/>
,然后找到</item>
,将其分组并替换为X
字符。
我还没有参加分组,我一直坚持在多行上做正则表达式。
在Mac OSX上运行
这就是我所拥有的:
perl -pn -e "s/<item>[\s\S]*<tag4 \/>/X/g" $XML_FILENAME > new_folder/$XML_FILENAME
如果我删除[\s\S]*
(这意味着任何空格字符或任何字符,我可以替换<item>
标记,但我无法转到下一个标记或下一行。
(我也试过echo//
和sed
陷入类似的位置)
答案 0 :(得分:3)
更好地使用实际的XML解析器(例如XML::LibXML
)并选择具有XPath表达式的空<tag4>
个节点:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::LibXML;
my $xml = XML::LibXML->new->parse_file('/path/to/input.xml');
$_->unbindNode for $xml->findnodes('//item[not(tag4/text())]');
print $xml->toString;
如果要将修改后的XML直接保存到文件中,请替换
行print $xml->toString;
与
$xml->toFile('/path/to/output.xml');
答案 1 :(得分:2)
试试这个:
s/<item>(?>[^<]++|<(?!tag4))*<tag4 \/>(?>[^<]++|<(?!\/item>))*<\/item>/X/g
这种模式避免了换行问题,因为它没有使用点。
说明
(?>[^<]++|<(?!tag4))*
(?> # open an atomic group
[^<]++ # all that is not a < one or more times (possessive)
| # OR
<(?!tag4) # a < not followed by tag4
)* # close the atomic group, repeat zero or more times
使用这个技巧,我确信接下来是<tag4
(或字符串的结尾)
我使用atomic groups (?>..)
和possessive quantifiers ++
来获得更多效果,但您可以将其替换为普通群组(?:..)
和贪婪量词{{1} }
通告
或者您可以使用延迟量词替换+
<{1}}
请注意,使用perl,您可以使用dotall模式而不是[\s\S]*
添加s修饰符:
[\s\S]*?
答案 2 :(得分:2)
使用正则表达式处理XML是不切实际的。您应该使用适当的Perl模块。
这个简短的程序使用XML::Twig
来处理名称作为命令行参数传递的文件。它将修改后的XML发送到STDOUT
。
use utf8;
use strict;
use warnings;
use XML::Twig;
my $twig= XML::Twig->new(pretty_print => 'indented');
$twig->parsefile($ARGV[0]);
for my $twig ($twig->findnodes('/root/item')) {
$twig->delete unless $twig->findvalue('tag4') =~ /\S/;
}
$twig->print;
<强>输出强>
<?xml version="1.0" encoding="utf-8"?>
<root>
<item>
<tag1>some text</tag1>
<tag2><![CDATA[http://url1.com]]></tag2>
<tag3/>
<tag4>not empty node</tag4>
</item>
</root>
答案 3 :(得分:0)
GNU awk
的一种方式:
awk '
BEGIN {
ORS=""
RS="<[/]?item>"
f1="<item>"
f2="<\/item>"
}
!/<tag4 \/>/ && NF {
print ($0~/tag/)?f1 $0 f2:$0
}' xmlfile
答案 4 :(得分:0)
这可行但需要一点工作:
perl -00 -ne 's/<item>.*<\/tag4>.*<\/item>/X/gs;print "$_\n";' test.xml