2013-06-26 35 views
1

我有一个包含XML数据文件:用Perl多行正则表达式(OSX)bash脚本

<?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。也就是说,我想删除每个子空间为<tag4>的元素<item>

因此,我想查找<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陷入类似的位置)

+0

必须逃脱斜线:'' –

+0

@CasimiretHippolyte当然,只是一个错字 – Blundell

回答

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++获得更多表演,但您可以将其替换为普通组(?:..)个贪婪量词+

声明

或者你也可以通过[\s\S]*?

需要注意的是用Perl你可以使用DOTALL模式,而不是[\s\S]加入s修正使用惰性限定符替换[\s\S]*

(?s).*   # the dot matches newlines 
(?-s).*   # the dot doesn't match newlines (default behavior) 
+0

不好意思,你的第一个注册表号不起作用。你能解释一下吗?也许我可以让它工作 – Blundell

+0

@Blundell:对不起,我犯了一个错误,它被纠正了。即将解释。 –

+0

作品我只需在's/..'之前添加'BEGIN {undef $ /;}'' – Blundell

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

更好地利用了这样的一个实际的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

'$ _-> unbindNode for $ xml-> findnodes('/ root/item [not(tag4/text())]')' – Borodin

+0

@Borodin更好。感谢您指出了这一点。 –

+0

不错的想法,没有意识到你可以在bash脚本中得到这个 – Blundell

0

一个与GNU awk方式:

awk ' 
BEGIN { 
    ORS="" 
    RS="<[/]?item>" 
    f1="<item>" 
    f2="<\/item>" 
} 
!/<tag4 \/>/ && NF { 
    print ($0~/tag/)?f1 $0 f2:$0 
}' xmlfile 
0

这个工作,但需要一点点的工作:

perl -00 -ne 's/<item>.*<\/tag4>.*<\/item>/X/gs;print "$_\n";' test.xml