在元素开始/结束标记中找到特定测试条件后删除特定的XML元素

时间:2017-09-10 17:06:19

标签: xml shell awk sed

我正在寻找创建一个快速shell脚本(HP-UX系统)来根据简单的测试条件删除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>

2 个答案:

答案 0 :(得分:0)

此脚本非常简单,可以与任何版本的awk一起使用:

read()

它检测起始标记并将两个布尔值设置为false,0和true,1,并清除字符串变量with open("global_users.txt", "r") as f: for data in f: if data == username_ask: print(G+"Success!") password_ask = raw_input(O+"Password:"+W+" ") with open("global_passwords.txt", "r") as f: for line in f: data = f.read() if data == password_ask: print(G+"Success!") else: print(R+"Incorrect Password!") else: print(R+"No Users Found!")
找到空标记后,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 } ' 布尔值设置为true。 要删除的组的开始和结束标记之间的所有行都在字符串变量中累积,它们之间有换行符。

如果不保存行,则打印它们。当结束标记匹配时, 如果未找到空标签,则打印已保存的组,然后停止保存。

代码中存在一些冗余,但它是为了保持简单。显然,它只处理你给出的格式的数据,而不是任何xml。

答案 1 :(得分:0)

对于多字符RS,使用GNU awk会更简洁:

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