尝试从多个文件中读取 XML 属性,并将其替换为比之前大 1.25 倍的数字

尝试从多个文件中读取 XML 属性,并将其替换为比之前大 1.25 倍的数字

我正在尝试编写一个 bash 脚本或类似的脚本,我可以用它一键修改一堆 xml 文件中的所有匹配项。我有 24 个 xml 文件,每个文件包含几十行,Torque="SOMENUMBER"每个文件中的属性大约有 3-5 次。我想用 1.25 倍的值替换那些,以便简单地为游戏中的每个电机添加 25% 的扭矩。

(这样做会很有用,因为模组和游戏补丁会覆盖这些,我可以快速尝试其他值。)

我想出了用于sed -n -e 's/Torque="\(.*\)"/\1/p' <filename.xml提取数字的想法,将其放入变量中,然后我不知道如何将其放回到正确的位置,并且sed上面的命令一次输出所有出现的扭矩。

xmlstarlet 可能可以根据我在 google 上搜索到的内容来做到这一点,但它总是抱怨 xml 文件包含多个根元素。也许我应该以某种方式在所有内容周围添加一个临时标签<root_temp><\root_temp>,让 xmlstarlet 发挥它的魔力,再次删除标签并保存文件?已经好几年了,自从我涉足 bash 脚本,我也愿意用 python、cpp 来做,学习一门新语言的基础知识,我不在乎 xD

XML 示例:

<_templates>
    <Engine>
        <RUScoutModernEngine BrakesDelay="0.5" />
    </Engine>
</_templates>
<EngineVariants>
    <Engine
        _template="RUScoutModernEngine"
        CriticalDamageThreshold="0.7"
        DamageCapacity="120"
        DamagedConsumptionModifier="1.2"
        EngineResponsiveness="0.35"
        FuelConsumption="1.5"
        Name="ru_scout_modern_engine_0"
        Torque="70000"
        DamagedMinTorqueMultiplier="1.0"
        DamagedMaxTorqueMultiplier="0.6"
        MaxDeltaAngVel="0.01"
    >
        <GameData
            Price="1900"
            UnlockByExploration="false"
            UnlockByRank="1"
        >
            <UiDesc
                PLACE="HOLDER"
            />
        </GameData>
    </Engine>
    <Engine
        BLA="BLA"
        Torque="80000"
        BLA="BLA"
    >
        <GameData
            Price="5500"
            UnlockByExploration="true"
            UnlockByRank="1"
        >
            <UiDesc
                PLACE="HOLDER"
            />
        </GameData>
    </Engine>
    <Engine
        BLA="BLA"
        Torque="76000"
        BLA="BLA"
    >
        <GameData
            BLA="BLA"
        >
            <UiDesc/>
        </GameData>
    </Engine>
</EngineVariants>

答案1

在损坏的 XML 周围添加假根节点非常容易,使用 修改修改后的 XML 会轻而易举xmlstarlet,并且在修改后删除添加的根节点(以及添加的<?xml version="1.0"?>根节点xmlstarlet)也不太难:

{ echo '<root>'; cat file.xml; echo '</root>'; }  |
xmlstarlet ed -u '//Engine/@Torque' -x '. * 1.25' |
sed '1d; 2d; $d'

显然,这假设该位产生的 XML{ ...; }实际上是格式良好的 XML 文档(示例文档中有重复的属性)。


与上面相同的事情,但是使用xq(一个围绕 的 XML 解析器jq,可作为yqat的一部分使用)https://kislyuk.github.io/yq/):

{ echo '<root>'; cat file.xml; echo '</root>'; }  |
xq -x '.root.EngineVariants.Engine[]."@Torque" |= ( tonumber * 1.25)' |
sed '1d; $d'

输出xq不包含<?xml version="1.0"?>.


*.xml对目录中名称匹配的所有文件重复此操作,使用xmlstarlet上述代码的变体作为示例:

tmpfile=$(mktemp)

for pathname in ./*.xml; do
    cp "$pathname" "$tmpfile" &&
    { echo '<root>'; cat "$tmpfile"; echo '</root>'; } |
    xmlstarlet ed -u '//Engine/@Torque' -x '. * 1.25'  |
    sed '1d; 2d; $d' >"$pathname"
done

rm -f "$tmpfile"

请注意,上面对匹配的文件进行了就地编辑。您可能想在复制首先是你的文件!

答案2

XML 片段(具有多个根节点的文件)非常难以管理,很少有工具直接接受它们。 “手动”添加包装元素是个好主意;另请注意中描述的技术

https://stackoverflow.com/questions/23571941/making-use-of-an-xml-file-with-more-than-one-root-element/23574398#23574398

一旦您有了格式良好的文档,就可以在 xmlstarlet 或 Saxon's Gizmo 中进行所需的更新(https://saxonica.com/documentation10/index.html#!gizmo)应该是简单的[声明自身利益]。在 Gizmo 中是

update //@Torque with .*1.25

答案3

我通过使用临时根节点实现该选项以使其有效,然后按照建议使用 Gizmo 解决了该问题。对于后来偶然发现这个问题的人,这是我的解决方案。

我下载的是java版本的撒克逊家庭版,将其解压到名为“Saxon”的子文件夹中,并成功使用以下脚本。

#!/bin/bash

#remove script and generate it with the following lines (that way I only need
#to include my one .sh file)
rm -f script
echo update //@Torque with .*1.25>script
echo save temp_updated.xml method=xml indent=yes>>script

#we do the following to all xml files in the subfolder "classes/engines/"
for filename in classes/engines/*.xml
do
    #cleanup in case they got leftover, which is important for Gizmo,
    #otherwise I get a thousand "Overwrite File" prompts, which is
    #not conducive to automating the process.
    rm -f temp.xml
    rm -f temp_updated.xml

    #make a new temp file, starting with the temporary root element,
    #append the original file and close the root element
    echo "<xml_temp>">temp.xml
    cat $filename>>temp.xml
    echo "</xml_temp>">>temp.xml

    #call Saxon with the correct codepath, start the Gizmo subroutine or whatever
    #take the temporary file as input and "script" as the commands to be executed
    java -cp "Saxon/saxon-he-10.5.jar:Saxon/jline-2.14.6.jar" net.sf.saxon.Gizmo -s:temp.xml -q:script
    #sed -i for inplace editing of the file
    #the first expression '$d' removes the last line (the closing temp root tag)
    #the second impression removes the first line (Gizmo tags on some kind of xml info
    #and the second line, which contains the starting temp root tag
    sed -i -e '$d' -e 1,2d temp_updated.xml

    #I force overwrite the old file with the new and improved xml
    cp -f temp_updated.xml $filename
    #...and clean up after myself
    rm -f temp.xml
    rm -f temp_updated.xml
    rm -f script
done

相关内容