Shell脚本 – 将xml分成多个文件

我试图将一个大的XML文件分割成多个文件,并在AWK脚本中使用了下面的代码。

/<fileItem>/ { rfile="fileItem" count ".xml" print "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > rfile print $0 > rfile getline while ($0 !~ "<\/fileItem>" ) { print > rfile getline } print $0 > rfile close(rfile) count++ } 

上面的代码生成一个名为“fileItem_1”,“fileItem_2”,“fileItem3”等的XML文件列表

不过,我希望文件名是类似于“item_XXXXX”,其中XXXXX是XML内的节点 – 如下所示

 <fileItem> <id>12345</id> <name>XXXXX</name> </fileItem> 

所以,基本上我想“id”节点是文件名。 任何人都可以帮我这个吗?

Solutions Collecting From Web of "Shell脚本 – 将xml分成多个文件"

我不会使用getline 。 (我甚至在一本AWK书中读过,不推荐使用它)。我认为,使用全局变量的状态更简单。 (具有全局变量的表达式也可以在模式中使用。)

脚本可能是这样的:

test-split-xml.awk

 /<fileItem>/ { collect = 1 ; buffer = "" ; file = "fileItem_"count".xml" ++count } collect > 0 { if (buffer != "") buffer = buffer"\n" buffer = buffer $0 } collect > 0 && /<name>.+<\/name>/ { # cut "...<name>" i = index($0, "<name>") ; file = substr($0, i + 6) # cut "</name>..." i = index(file, "</name>") ; file = substr(file, 1, i - 1) file = file".xml" } /<\/fileItem>/ { collect = 0; print file print "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" >file print buffer >file } 

我准备了一些小样本的样本数据:

test-split-xml.xml

 <?xml version=\"1.0\" encoding=\"UTF-8\"?> <top> <some> <fileItem> <id>1</id> <name>X1</name> </fileItem> </some> <fileItem> <id>2</id> <name>X2</name> </fileItem> <fileItem> <id>2</id> <!--name>X2</name--> </fileItem> <any> other input </any> </top> 

…并得到以下输出:

 $ awk -f test-split-xml.awk test-split-xml.xml X1.xml X2.xml fileItem_2.xml $ more X1.xml <?xml version="1.0" encoding="UTF-8"?> <fileItem> <id>1</id> <name>X1</name> </fileItem> $ more X2.xml <?xml version="1.0" encoding="UTF-8"?> <fileItem> <id>2</id> <name>X2</name> </fileItem> $ more fileItem_2.xml <?xml version="1.0" encoding="UTF-8"?> <fileItem> <id>2</id> <!--name>X2</name--> </fileItem> $ 

tripleee的评论是合理的。 因此,这种处理应该限于个人使用,因为不同的(和合法的)XML文件的格式化可能会在这个脚本处理中造成错误。

你会注意到,整个脚本中没有next 。 这是故意的。

首先 – 你需要一个解析器。

XML是一种上下文数据格式。 正则表达式不是。 所以你永远不能使一个正则表达式的基础处理系统实际上正常工作。

这只是坏消息

但解析器确实存在,而且它们很容易处理。 我可以给你一个更好的数据输入例子。 但我会使用XML::Twigperl来做到这一点:

 #!/usr/bin/env perl use strict; use warnings; use XML::Twig; #subroutine to extract and process the item sub save_item { my ( $twig, $item ) = @_; #retrieve the id my $id = $item -> first_child_text('id'); print "Got ID of $id\n"; #create a new XML document for output. my $new_xml = XML::Twig -> new; $new_xml -> set_root (XML::Twig::Elt -> new ( 'root' )); #cut and paste the item from the 'old' doc into the 'new' #note - "cut" applies to in memory, #not the 'on disk' copy. $item -> cut; $item -> paste ( $new_xml -> root ); #set XML params (not strictly needed but good style) $new_xml -> set_encoding ('utf-8'); $new_xml -> set_xml_version ('1.0'); #set output formatting $new_xml -> set_pretty_print('indented_a'); print "Generated new XML:\n"; $new_xml -> print; #open a file for output open ( my $output, '>', "item_$id.xml" ) or warn $!; print {$output} $new_xml->sprint; close ( $output ); } #create a parser. my $twig = XML::Twig -> new ( twig_handlers => { 'fileItem' => \&save_item } ); #run this parser on the __DATA__ filehandle below. #you probably want parsefile('some_file.xml') instead. $twig -> parse ( \*DATA ); __DATA__ <xml> <fileItem> <id>12345</id> <name>XXXXX</name> </fileItem> </xml> 

随着XML::Twigxml_split这可能适合您的需求

如果你的XML真的很好的形成和一致的,那么你所需要的就是:

 awk -F'[<>]' ' /<fileItem>/ { header="<?xml version=\"1.0\" encoding=\"UTF-8\"?>" ORS $0; next } /<id> { close(out); out="item_" $3; $0=header ORS $0 } { print > out } ' file 

以上是没有经过测试的,因为您没有提供样本输入/输出以供我们测试可能的解决方案。