sed를 사용하여 xml 파일에서 n번째 패턴 발생을 찾아 바꾸기

sed를 사용하여 xml 파일에서 n번째 패턴 발생을 찾아 바꾸기

내 ova에는 두 개의 vmdk 파일이 있습니다. ovf 파일은 두 vmdk 파일의 기능을 포함하는 xml입니다. 파일의 내용은 다음과 같습니다

<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>

VirtualQuantity 요소도 포함하는 VMDK2의 메모리 구성을 수정하고 싶지 않습니다.

아래에서 시도했지만 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</>. 그런 다음 $ncpusCPU 수를 셸 변수 값(예제에서는 16)으로 바꿉니다. 이러한 매개변수는 XML 데이터를 올바르게 구문 분석하는 -N {namespace}데 필요합니다 . xmlstarlet(간단한 단일 네임스페이스의 경우 일반적으로 기본 _이름을 사용할 수 있지만 여기서는 다양한 요소의 네임스페이스를 명시적으로 식별해야 합니다.)

합법적인 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"

(하드 링크와 메타데이터에 관심이 없다면 이를 단순화하고 cat로 대체할 수 있습니다 mv -f.)

답변2

쉘 변수 $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

관련 정보