2017-09-10 38 views
1

我期待創建一個基於簡單測試條件的快速shell腳本(HP-UX系統)來刪除XML標記。由於種種原因,我無法使用XML感知工具,如'xmlstarlet',因爲這些工具在我的生產系統上不可用。我意識到這些是正確的路,但我在這件事上沒有選擇。在元素開始/結束標記中查找特定測試條件後刪除特定的XML元素

考慮下面兩個關於兩個設備的XML元素。當設備不通電時,不會有StationId,也不會有HardwareInv,標籤與<..../>不同。當設備處於通信狀態時,StationId存在且HardwareInv內容可用,則開始/結束標記完成,即結束時爲</....>

我想找到並通過搜索<StationId/>和/或<HardwareInv/>,如果找到,完全刪除相關DeviceA標籤,包括DeviceA之間的所有內容標籤本身不留空白行的後面取出裝置外的通訊科。

我已經嘗試了幾個不同的結果,特別是使用'sed',但沒有100%成功。非常感謝您的幫助。

這是輸入XML文件:

<DeviceA> 
    <PhysicalAdd>10.10.10.69</PhysicalAdd> 
    <NEId>0000-Test-06</NEId> 
    <StationId/> 

    *** MORE CONTENT REMOVED *** 

    <HardwareInv/> 
</DeviceA> 
<DeviceA> 
    <PhysicalAdd>10.10.10.109</PhysicalAdd> 
    <NEId>0000-Test-13</NEId> 
    <StationId>Bravo-01</StationId> 

    *** MORE CONTENT REMOVED *** 

    <HardwareInv> 
    <Unit> 
     <UnitId>1</UnitId> 
     <SerialNumber>1389A</SerialNumber> 
    </Unit> 
    </HardwareInv> 
</DeviceA> 

預期的輸出:

<DeviceA> 
    <PhysicalAdd>10.10.10.109</PhysicalAdd> 
    <NEId>0000-Test-13</NEId> 
    <StationId>Bravo-01</StationId> 

    *** MORE CONTENT REMOVED *** 

    <HardwareInv> 
    <Unit> 
     <UnitId>1</UnitId> 
     <SerialNumber>1389A</SerialNumber> 
    </Unit> 
    </HardwareInv> 
</DeviceA> 
+0

@EdMorton ,感謝您的輸入,將更新帖子。它是一個大文件的一部分,並將用作輸入。預期的輸出在解釋中解釋,但會以任何方式更新。 – Monty

+0

@EdMorton,不幸的是沒有GNU awk可用。 – Monty

回答

0

這個腳本是很簡單的與任何版本的awk的工作:

awk ' 
/<DeviceA>/   { found = 0; tosave = 1; save = "" } 
/<HardwareInv\/>/ || /<StationId\/>/  { found = 1 } 
/<DeviceA>/,/<\/DeviceA>/ { save = save $0 "\n" } 
tosave==0   { print } 
/<\/DeviceA>/  { if(!found)printf "%s",save; tosave = 0 } 
' 

它檢測起始標記並將兩個布爾值設置爲false,0和true,並清除一個字符串變量save
當找到空標籤時,found布爾值被設置爲true。 要刪除的組的開始標記和結束標記之間的所有行都會在字符串變量中累積,並在它們之間換行。

如果沒有保存行,然後打印它們。結束標記匹配時,如果未找到空標記,則輸入 ,打印已保存的組並停止保存。

代碼中有一些冗餘,但它保持簡單。顯然,這隻處理你給出的格式的數據,並不適用於任何xml。

+0

我發現POSIX awk(HP-UX系統)的問題,我以前沒有遇到過,它顯示有300字節的硬限制(這是錯誤:「awk:格式項目%s不能超過3,000字節「。 )當對'真正'文件運行腳本時。顯然,所示的示例缺少內容,因此在給出的示例中不明顯。我確實設法發現,我們的兩個HP-UX系統確實存在gawk並作爲gawk腳本運行,都像夢一樣運行(不受POSIX awk的限制),所以感謝您的解決方案! – Monty

+0

有趣。如果問題只是因爲'%s'太長了,簡單的答案就是用'print substr'(save,1,length(save)-1)'替換'printf'%s',save'。我只是用它來刪除最後的換行符,因爲print會添加最後的換行符。 – meuh

+0

是的,最後一個建議與POSIX AWK一起工作:-)罰款,需要稍長的時間才能完成但工作正常,再次,非常感謝! – Monty

0

它會用GNU AWK進行簡短一些多焦RS:

$ awk -v RS='</DeviceA>\\s*' -v ORS= '/<StationId>/{print $0 RT}' file 
<DeviceA> 
    <PhysicalAdd>10.10.10.109</PhysicalAdd> 
    <NEId>0000-Test-13</NEId> 
    <StationId>Bravo-01</StationId> 

    *** MORE CONTENT REMOVED *** 

    <HardwareInv> 
    <Unit> 
     <UnitId>1</UnitId> 
     <SerialNumber>1389A</SerialNumber> 
    </Unit> 
    </HardwareInv> 
</DeviceA> 

但任何awk的,你只需要首先建立了由行中的記錄行:

$ cat tst.awk 
{ rec = (rec=="" ? "" : rec ORS) $0 } 
/<\/DeviceA>/ { 
    if (rec ~ /<StationId>/) { 
     print rec 
    } 
    rec = "" 
} 

$ awk -f tst.awk file 
<DeviceA> 
    <PhysicalAdd>10.10.10.109</PhysicalAdd> 
    <NEId>0000-Test-13</NEId> 
    <StationId>Bravo-01</StationId> 

    *** MORE CONTENT REMOVED *** 

    <HardwareInv> 
    <Unit> 
     <UnitId>1</UnitId> 
     <SerialNumber>1389A</SerialNumber> 
    </Unit> 
    </HardwareInv> 
</DeviceA> 
+0

感謝您花時間提供解決方案。根據其他解決方案的評論,POSIX awk存在一個問題。我試過這個解決方案,但是如果沒有產生任何輸出,即使使用gawk。我還沒有分析爲什麼,但其他解決方案確實工作,儘管使用gawk。 – Monty

+0

由於您擁有GNU awk,因此我添加了GNU awk解決方案,並且放鬆了記錄結尾的文本以允許拖尾空白並將printf替換爲打印。 –

+1

是的,我同意,你的解決方案對粘貼的內容工作正常。我接受67 MB的「真實」文件並且增長的內容更多,因此發佈的任何潛在解決方案都可能因此失敗。我發佈了我確定可以陷入/尋找的「變化」的部分。我創建了一個更大的腳本,但需要一些人的幫助,這些人在這個特定的位上比我自己有更多的sed/awk知識。非常感謝您的幫助,非常感謝! – Monty