2017-09-10 58 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