2017-11-11 154 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?) –