使用Perl多行正则表达式(OSX)的Bash脚本

时间:2013-06-26 15:22:36

标签: regex perl bash multiline

我有一个包含此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陷入类似的位置)

5 个答案:

答案 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