簡體   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