使用 sed 查找并替换 xml 文件中第 n 次出现的模式

使用 sed 查找并替换 xml 文件中第 n 次出现的模式

我的 ova 中有两个 vmdk 文件。 ovf 文件是一个 xml,包含两个 vmdk 文件的功能。文件内容如下

<ovf:VirtualSystem ovf:id="VMDK1">

                    <ovf:Item>
                    <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
                    <rasd:Description>Number of Virtual CPUs</rasd:Description>
                    <rasd:ElementName>8 virtual CPU(s)</rasd:ElementName>
                    <rasd:InstanceID>6</rasd:InstanceID>
                    <rasd:Reservation>9000</rasd:Reservation>
                    <rasd:ResourceType>3</rasd:ResourceType>
                    <rasd:VirtualQuantity>8</rasd:VirtualQuantity>
                    <rasd:Weight>8000</rasd:Weight>
                    <vmw:CoresPerSocket ovf:required="false">8</vmw:CoresPerSocket>
                </ovf:Item>
                <ovf:Item>
                    <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
                    <rasd:Description>Memory Size</rasd:Description>
                    <rasd:ElementName>12288 MB of memory</rasd:ElementName>
                    <rasd:InstanceID>7</rasd:InstanceID>
                    <rasd:Reservation>12288</rasd:Reservation>
                    <rasd:ResourceType>4</rasd:ResourceType>
                    <rasd:VirtualQuantity>12288</rasd:VirtualQuantity>
                    <rasd:Weight>122880</rasd:Weight>
                </ovf:Item>
</ovf:VirtualSystem>
<ovf:VirtualSystem ovf:id="VMDK2">

                    <ovf:Item>
                    <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
                    <rasd:Description>Number of Virtual CPUs</rasd:Description>
                    <rasd:ElementName>14 virtual CPU(s)</rasd:ElementName>
                    <rasd:InstanceID>7</rasd:InstanceID>
                    <rasd:Reservation>9000</rasd:Reservation>
                    <rasd:ResourceType>3</rasd:ResourceType>
                    <rasd:VirtualQuantity>14</rasd:VirtualQuantity>
                    <rasd:Weight>14000</rasd:Weight>
                    <vmw:CoresPerSocket ovf:required="false">14</vmw:CoresPerSocket>
                </ovf:Item>
                <ovf:Item>
                    <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
                    <rasd:Description>Memory Size</rasd:Description>
                    <rasd:ElementName>15360 MB of memory</rasd:ElementName>
                    <rasd:InstanceID>8</rasd:InstanceID>
                    <rasd:Reservation>15360</rasd:Reservation>
                    <rasd:ResourceType>4</rasd:ResourceType>
                    <rasd:VirtualQuantity>15360</rasd:VirtualQuantity>
                    <rasd:Weight>153600</rasd:Weight>
                </ovf:Item>

</ovf:VirtualSystem>

使用 sed 我想仅更改 VMDK2 的以下 CPU 配置。

<rasd:ElementName>14 virtual CPU(s)</rasd:ElementName>
<rasd:VirtualQuantity>14</rasd:VirtualQuantity>
<vmw:CoresPerSocket ovf:required="false">14</vmw:CoresPerSocket>

请注意,我不想修改 VMDK2 的任何内存配置,VMDK2 也具有 VirtualQuantity 元素。

我在下面尝试过,但它同时修改了 VMDK1 和 VMDK2 CPU 配置。

sed -i "/hertz/,/CoresPerSocket/s/[0-9]\+ virtual CPU/$cores virtual CPU/
/hertz/,/CoresPerSocket/s%<rasd:VirtualQuantity>[0-9]\+</rasd:VirtualQuantity>%<rasd:VirtualQuantity>$cores</rasd:VirtualQuantity>%
/CoresPerSocket/s/[0-9]\+/${cores}/" $ovf

答案1

以下是使用理解 XML 的 XML 解析器正确执行此操作的方法。

ncpus=16
xmlstarlet edit \
    -N xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" \
    -N xmlns:vcloud="http://www.vmware.com/vcloud/v1.5" \
    -N xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" \
    -N xmlns:vmw="http://www.vmware.com/schema/ovf" \
    --var vi '//ovf:VirtualSystem[@ovf:id="VMDK2"]/ovf:Item[rasd:Description="Number of Virtual CPUs"]' \
    --update '$vi/rasd:ElementName' --value "$ncpus virtual CPU(s)" \
    --update '$vi/rasd:VirtualQuantity' --value "$ncpus" \
    --update '$vi/vmw:CoresPerSocket' --value "$ncpus" \
    ova.ovf

这会挑选出 的块<ovf:VirtualSystem id="VMDK2"/>,然后挑选<ovf:Item/>出包含 的小节<rasd:Description>Number of Virtual CPUs</>。然后它用 shell 变量的值$ncpus(在我的示例中为 16)替换 CPU 计数。-N {namespace}需要这些参数才能xmlstarlet正确解析 XML 数据。 (对于简单的单个命名空间,我们通常可以使用默认_名称,但这里我们需要显式标识各种元素的命名空间。)

我必须修改您的原始示例以包含足够的 XML 以使其成为合法的 XML 文件:

<Item xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"
   xmlns:vcloud="http://www.vmware.com/vcloud/v1.5"
   xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"
   xmlns:vmw="http://www.vmware.com/schema/ovf"
>
<ovf:VirtualSystem ovf:id="VMDK1">

…your examples here…

</ovf:VirtualSystem>
</Item>

与许多工具一样,该命令将其编辑后的输出生成为标准输出。您可以使用到位如果您确实想要的话,请标记 - 但在测试时肯定不会 - 但我通常的偏好是将文件保存为备份并将编辑后的结果写入原始名称。这种方法适用于任何工具,保持硬链接、权限和其他可能存在于原始版本中的元数据不变:

xmlstarlet ... "original_file" >"original_file.$$.tmp" &&
    cp -fp "original_file" "backup_file" &&
    cat "original_file.$$.tmp" >"original_file"
rm -f "original_file.$$.tmp"

(如果您不关心硬链接和元数据,您可以简化这一点,将 替换catmv -f。)

答案2

假设您的 shell 变量$cores保存一个已动态分配给它的值,即: cores=$(grep -c processor /proc/cpuinfo),那么您可以使用类似以下内容的内容:

awk -v RS='</ovf:VirtualSystem>' \
    -v ORS='</ovf:VirtualSystem>' \
    -v cores="$cores" \
    '/ovf:id="VMDK2"/ {
        sub(/<rasd:VirtualQuantity>[0-9]+<\/rasd:VirtualQuantity>/, "<rasd:VirtualQuantity>" cores "</rasd:VirtualQuantity>");
        sub(/<rasd:ElementName>[0-9]+ virtual CPU\(s\)<\/rasd:ElementName>/, "<rasd:ElementName>" cores " virtual CPU(s)</rasd:ElementName>");
    }
    NR > 1 { 
        print prev_rs 
    }
    {
        prev_rs = $0
    }
    END {
        printf("%s", prev_rs)
    }' your_xml_file.xml

相关内容