2017-11-11 159 views
4

這是一個效率問題,而不是故障排除。我有下面的代碼片段:用XMLStarlet插入1000多個節點和屬性 - 運行緩慢

# The -R flag restores malformed XML 
xmlstarlet -q fo -R <<<"$xml_content" | \ 
    # Delete xml_data 
    xmlstarlet ed -d "$xml_data" | \ 
    # Delete index 
    xmlstarlet ed -d "$xml_index" | \ 
    # Delete specific objects 
    xmlstarlet ed -d "$xml_nodes/objects" | \ 
    # Append new node 
    xmlstarlet ed -s "$xml_nodes" -t elem -n subnode -v "Hello World" | \ 
     # Add x attribute to node 
     xmlstarlet ed -i "($xml_nodes)[last()]" -t attr -n x -v "0" | \ 
     # Add y attribute to node 
     xmlstarlet ed -i "($xml_nodes)[last()]" -t attr -n y -v "0" | \ 
     # Add z attribute to node 
     xmlstarlet ed -i "($xml_nodes)[last()]" -t attr -n z -v "1" \ 
      > "$output_file" 
  • 變量$xml_content包含內容的XML樹和
    節點從文件中解析大小爲472.6 MB使用cat命令。

  • 變量$output_file如其名稱所示,包含輸出文件的路徑 。

  • 其餘的變量只包含我想編輯的相應XPath。

根據這個簡短的article這幫助想出這個代碼,它表明:

這是一個有點ineffeciant因爲XML文件進行解析,並寫入兩次。

在我的情況下,它被解析並寫入兩次以上(最終在loop以上1000次)。

因此,採取上述腳本,該短片段的執行時間僅爲4分7秒。

與文件大小一起假設過多,重複和低效也許管道就是爲什麼代碼運行速度慢,越子節點我最終插入/刪除最終會導致它更慢執行。

如果我通過重申自己或者提出一個陳舊而可能已經回答的話題來聲音單調,我會提前道歉,但是,我真的很想了解xmlstarlet如何詳細處理大型XML文檔。


UPDATE

正如他在回答之前聲稱@Cyrus:

這兩個xmlstarlets應該做的工作:

xmlstarlet -q fo -R <<<"$xml_content" |\ 
    xmlstarlet ed \ 
    -d "$xml_data" \ 
    -d "$xml_index" \ 
    -d "$xml_nodes/objects" \ 
    -s "$xml_nodes" -t elem -n subnode -v "Hello World" \ 
    -i "($xml_nodes)[last()]" -t attr -n x -v "0" \ 
    -i "($xml_nodes)[last()]" -t attr -n y -v "0" \ 
    -i "($xml_nodes)[last()]" -t attr -n z -v "1" > "$output_file" 

這產生的以下錯誤:

  • -:691.84: Attribute x redefined
  • -:691.84: Attribute z redefined
  • -:495981.9: xmlSAX2Characters: huge text node: out of memory
  • -:495981.9: Extra content at the end of the document

我真的不知道如何將這些錯誤在那裏生產的,因爲我改變了代碼過於頻繁測試各種情景和潛在的替代品,然而,這是我的伎倆:

xmlstarlet ed --omit-decl -L \ 
    -d "$xml_data" \ 
    -d "$xml_index" \ 
    -d "$xml_nodes/objects" \ 
    -s "$xml_nodes" -t elem -n subnode -v "Hello World" \ 
    "$temp_xml_file" 

xmlstarlet ed --omit-decl -L \ 
    -i "($xml_nodes)[last()]" -t attr -n x -v "0" \ 
    -i "($xml_nodes)[last()]" -t attr -n y -v "0" \ 
    -i "($xml_nodes)[last()]" -t attr -n z -v "1" \ 
    "$temp_xml_file" 

關於所插入的實際data,這是我在開頭:

... 
<node> 
    <subnode>A</subnode> 
    <subnode>B</subnode> 
    <objects>1</objects> 
    <objects>2</objects> 
    <objects>3</objects> 
    ... 
</node> 
... 

執行上述(分裂)的代碼給了我什麼,我想:

... 
<node> 
    <subnode>A</subnode> 
    <subnode>B</subnode> 
    <subnode x="0" y="0" z="1">Hello World</subnode> 
</node> 
... 

通過分裂它們的xmlstarlet能夠將attributes插入到新創建的節點中,否則它將在創建--subnode之前將它們添加到所選Xpath的last()實例中。在某種程度上,這仍然是低效的,但是,代碼現在不到一分鐘。

下面的代碼,

xmlstarlet ed --omit-decl -L \ 
    -d "$xml_data" \ 
    -d "$xml_index" \ 
    -d "$xml_nodes/objects" \ 
    -s "$xml_nodes" -t elem -n subnode -v "Hello World" \ 
    -i "($xml_nodes)[last()]" -t attr -n x -v "0" \ 
    -i "($xml_nodes)[last()]" -t attr -n y -v "0" \ 
    -i "($xml_nodes)[last()]" -t attr -n z -v "1" \ 
    "$temp_xml_file" 

然而,使我這個:

... 
<node> 
    <subnode>A</subnode> 
    <subnode x="0" y="0" z="1">B</subnode> 
    <subnode>Hello World</subnode> 
</node> 
... 

通過加入xmlstarlets成一個像這樣post也@Cyrus回答,但也算是先加attributes然後創建--subnode,其中innerTextHello World

  • 任何人都可以解釋爲什麼這種奇怪的行爲發生?

這是另一種reference其中規定:「每個編輯操作順序執行」

上述文章解釋正是我要找的,但我不能管理,使工作都在一個xmlstarlet ed \。可替換地,我想:

  • 在此answer更換($xml_nodes)[last()]$xml_nodes[text() = 'Hello World']
  • 使用$prev(或$xstar:prev)作爲參數-i等。 [Examples]
  • 通過-rtemporary element name招重命名臨時節點attr添加

上述所有插入的--subnode後,並保留新元素而不attributes

注意:我在OS X埃爾卡皮坦V上運行XMLStarlet 1.6.1 10.11.3


BONUS

正如我在開頭提到的我希望用一個loop像沿着這些線:

list="$(tr -d '\r' < $names)" 

for name in $list; do 
    xmlstarlet ed --omit-decl -L \ 
    -d "$xml_data" \ 
    -d "$xml_index" \ 
    -d "$xml_nodes/objects" \ 
    -s "$xml_nodes" -t elem -n subnode -v "$name" \ 
    -i "($xml_nodes)[last()]" -t attr -n x -v "0" \ 
    -i "($xml_nodes)[last()]" -t attr -n y -v "0" \ 
    -i "($xml_nodes)[last()]" -t attr -n z -v "1" \ 
    "$temp_xml_file" 
done 

$list包含超過一千個不同的名稱,需要分別添加它們的attributes。每個屬性的--value也可能隨着每個loop而變化。鑑於上述模型:

  • 什麼是這樣loop的最快,最準確的版本給出的屬性被正確添加到相應的節點?

  • 在外部txt文件中創建節點列表並稍後將這些xml元素(在txt文件內)添加到另一個XML文件會更快嗎?如果是,如何?也許用sedgrep

關於最後一個問題,我指的是像this。應該添加來自txt的xml的節點必須是特定的,例如,至少可以選擇XPath,因爲我只想編輯某些節點。

注意:上述模型只是一個例子。實際的loop將爲每個loop添加26 --subnodes,並且每個--subnode添加3或4個attr。這就是爲什麼xmlstarlet正確添加attr而非其他元素很重要。他們必須按順序添加。

+0

我更新了我與例如輸入/輸出問題,就像你所說@Cyrus –

回答

2

爲什麼不使用parallel(或sem),以便可以將作業與機器上可用的內核數進行並行處理? 我使用的代碼是用2個變量解析一個數組,我在本地輸出以確保進程是孤立的。

for array in "${listofarrays[@]}"; do 
    local var1;local var2 
    IFS=, read var1 var2 <<< $array 
    sem -j +0 
    <code goes here> 
done 
sem --wait 
+0

感謝@ user1747036但我也想知道我可以正確添加'attr'。你有什麼建議嗎?我其實非常喜歡閱讀數組的想法。你可以在輸入和輸出看起來像什麼嗎? –

+0

順便說一句我在這[帖子](https://stackoverflow.com/questions/33653844/xmlstarlet-sel-on-large-file),它說,這些文件被讀入內存。平行xmlstarlets會不會吞噬我所有的記憶?只需使用xmlstarlet導入一次完整的節點列表(包括attr),就像這個[answer](https://stackoverflow.com/questions/23234681/adding-xml-element-in-xml-file-using-sed- command-in-shell-script#answer-23235535)聽起來很合適,但我如何選擇正確的節點才能將元素添加爲子節點? –

+0

類似於:'$ sed'/ <\/Students>/{r add.txt a \ d}'file',但用正確的(也許是唯一的)節點替換了''(使用XPath?) –