Shell脚本 - 将xml拆分为多个文件

时间:2017-03-06 12:33:41

标签: xml linux shell unix awk

我试图将一个大的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++
}

上面的代码生成一个xml文件列表,其名称为&#34; fileItem_1&#34;,&#34; fileItem_2&#34;,&#34; fileItem3&#34;等。

但是,我希望文件名类似&#34; item_XXXXX&#34;其中XXXXX是XML中的一个节点 - 如下所示

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

所以,基本上我想要&#34; id&#34;节点是文件名。 有人可以帮我这个吗?

3 个答案:

答案 0 :(得分:2)

我不会使用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。这是故意的。

答案 1 :(得分:2)

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

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

它是just bad news

但解析器确实存在,并且它们很容易使用。我可以通过更好的数据输入为您提供更好的示例。但我会使用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可能适合您的需要

答案 2 :(得分:1)

如果您的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

上述情况当然未经测试,因为您没有为我们提供样本输入/输出来测试可能的解决方案。