[英]Bash script with Perl multi-line regex (OSX)
我有一个包含以下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
char。
我什至还没有达到分组的目的,我被困在多行正则表达式上。
在Mac OSX上运行
这就是我得到的:
perl -pn -e "s/<item>[\s\S]*<tag4 \/>/X/g" $XML_FILENAME > new_folder/$XML_FILENAME
如果删除[\\s\\S]*
(表示任何空格字符或任何字符,我都可以替换<item>
标记,但无法进入下一个标记或下一行。
(我也尝试过echo//
并将sed
卡在类似的位置)
为此,最好使用实际的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');
尝试这个:
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
是<tag4
(或字符串的结尾)
我使用原子组 (?>..)
和所有格量词 ++
以获得更多性能,但是您可以将它们替换为普通组(?:..)
和贪婪的量词+
告示
或者,您可以只使用一个惰性量词将[\\s\\S]*
替换为[\\s\\S]*?
请注意,使用perl时,可以使用dotall模式,而不是[\\s\\S]
添加s修饰符:
(?s).* # the dot matches newlines
(?-s).* # the dot doesn't match newlines (default behavior)
使用正则表达式处理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>
使用GNU awk
一种方法:
awk '
BEGIN {
ORS=""
RS="<[/]?item>"
f1="<item>"
f2="<\/item>"
}
!/<tag4 \/>/ && NF {
print ($0~/tag/)?f1 $0 f2:$0
}' xmlfile
这可行,但需要一点工作:
perl -00 -ne 's/<item>.*<\/tag4>.*<\/item>/X/gs;print "$_\n";' test.xml
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.