使用XMLStarlet插入1000多个节点和属性-运行缓慢

Ava*_*lla 5 xml unix bash shell xmlstarlet

这是效率问题,而不是故障排除。我有以下代码片段:

# 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"
Run Code Online (Sandbox Code Playgroud)
  • 该变量$xml_content包含
    使用cat命令从大小为472.6 MB的文件中解析的内容和节点的xml树。

  • 变量$output_file如其名称所示,包含输出文件的路径。

  • 其余变量仅包含我要编辑的相应XPath。

根据帮助编写此代码的这篇简短文章,它表明:

由于xml文件被解析并写入了两次,因此这有点效率低下。

就我而言,它被解析和写入两次以上(最终loop超过1000次)。

因此,以上面的脚本为例,该短片段的执行时间仅为4分7秒。

假设过多的,重复的,可能是效率低下的管道传输以及文件大小是代码运行缓慢的原因,那么我最终插入/删除的子节点越多,最终将使其执行得越慢。

如果要重申自己的观点或提出一个古老且可能已经回答的话题,我可能会单调,我事先表示歉意,但是,我真的很想了解xmlstarlet大型XML文档如何详细工作。


更新

正如@Cyrus在他先前的回答中所声称的:

这两个xmlstarlet应该可以完成工作:

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"
Run Code Online (Sandbox Code Playgroud)

这产生了以下错误:

  • -: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"
Run Code Online (Sandbox Code Playgroud)

关于实际data插入的内容,这是我开始时的内容:

...
<node>
    <subnode>A</subnode>
    <subnode>B</subnode>
    <objects>1</objects>
    <objects>2</objects>
    <objects>3</objects>
    ...
</node>
...
Run Code Online (Sandbox Code Playgroud)

执行上面的(拆分)代码会给我我想要的东西:

...
<node>
    <subnode>A</subnode>
    <subnode>B</subnode>
    <subnode x="0" y="0" z="1">Hello World</subnode>
</node>
...
Run Code Online (Sandbox Code Playgroud)

通过拆分它们,xmlstarlet可以将插入attributes到新创建的节点中,否则它将在创建之前将它们添加到last()所选Xpath 的实例中--subnode。从某种程度上来说,这仍然是低效的,但是代码现在不到一分钟就能运行。

以下代码,

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"
Run Code Online (Sandbox Code Playgroud)

但是,给我这个:

...
<node>
    <subnode>A</subnode>
    <subnode x="0" y="0" z="1">B</subnode>
    <subnode>Hello World</subnode>
</node>
...
Run Code Online (Sandbox Code Playgroud)

通过xmlstarlets将此帖子加入一个同样由@Cyrus回答的帖子中的内容,它会以某种方式首先添加attributes,然后创建is --subnode所在的innerText位置Hello World

  • 谁能解释为什么这种奇怪的行为发生了?

这是另一个引用,指出“ 每个编辑操作都按顺序执行

上面的文章准确地解释了我要寻找的内容,但是我无法使其完全合用xmlstarlet ed \。另外,我尝试:

  • 替换($xml_nodes)[last()]$xml_nodes[text() = 'Hello World']
  • 在这个答案中使用$prev(或$xstar:prev)作为论点。[例子]-i
  • 所述临时元件名称经由特技-r重命名临时节点的后attr添加

以上所有内容均插入,--subnode但不添加则保留新元素attributes

注意:我在OS X El Capitan v 10.11.3上运行XMLStarlet 1.6.1


奖金

正如我在开始时提到的,我希望在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
Run Code Online (Sandbox Code Playgroud)

$list包含需要与各自的添加超过一千不同的名称attributes。所述--value各属性的可与每变化loop为好。鉴于以上模型:

  • loop如果将属性正确添加到相应的节点,那么最快和最准确的版本是什么?

  • 在外部txt文件中创建节点列表,然后将这些xml元素(在txt文件内部)添加 另一个XML文件中,会更快吗?如果是,怎么办?也许与sedgrep

关于最后一个问题,我指的是像这样。必须xml从txt中添加txt 的节点必须是特定的,例如,至少可以由XPath选择,因为我只想编辑某些节点。

注意:以上模型仅是示例。实际loop将添加26 --subnodes每个loop和3或4个attr为每个--subnode。这就是为什么正确xmlstarlet添加attr而不是其他元素很重要的原因。必须按顺序添加它们。

小智 2

为什么不使用并行(或 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
Run Code Online (Sandbox Code Playgroud)