繁体   English   中英

具有Perl多行正则表达式(OSX)的Bash脚本

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM