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