2017-03-06 99 views
1

我正在嘗試將一個大的xml文件拆分爲多個文件,並在AWK腳本中使用了以下代碼。Shell腳本 - 將xml拆分爲多個文件

/<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文件,其名稱改爲「fileItem_1」,「fileItem_2」,「fileItem3」列表等

不過,我想文件名類似於「 item_XXXXX」,其中XXXXX是XML中的一個節點 - 描述如下

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

所以,基本上我想要的‘ID’節點是文件名。 任何人都可以請幫助我嗎?

+1

聽起來像你應該真的使用適當的XML工具。如果你的輸入是完全正常的,那麼使用Awk一次將一條記錄讀入內存,並在達到它的結束標記(或文件末尾,儘管違反了XML)時將其刷新到磁盤,可能會讓你做你想做的事。簡而言之,當您看到開始標籤時將變量設置爲1,然後在變量爲true時將行累加到另一個變量中;最後,當你看到結束標籤時,寫出累計行並將變量設置回0.這是一種非常標準的Awk技術,因此示例不應該很難找到 – tripleee

+0

如果您曾經考慮過使用getline腳本,然後確保你先閱讀並完全理解http://awk.freeshell.org/AllAboutGetline,這樣你就知道你正在進入什麼。 –

+0

對於將來的讀者,一般解決方案比'awk'命令複雜得多......使用XML工具,使用XML庫的高級語言,使用結果文檔的XSLT V2.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。這是故意的。

+0

這樣工作對我來說Scheff。謝謝 – rosebrit3

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可以適合您的需求

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 

以上未經測試當然,因爲您沒有提供樣本輸入/輸出以供我們測試可能的解決方案。

相關問題