Nexpose XML结果解析器中的Ruby/Nokogiri嵌套循环失败

Nexpose XML结果解析器中的Ruby/Nokogiri嵌套循环失败,ruby,xml,nokogiri,Ruby,Xml,Nokogiri,我正在尝试编写一个ruby脚本,以导出Nexpose简单XML结果,对其进行解析,并将所需结果以更漂亮的格式输出,以便于查看。我正在使用Nokogiri解析XML。我的问题是,我有一个嵌套循环,用于每个设备,迭代每个服务部分,并从每个服务部分提取名称、端口和协议属性。最终将打印回文本文件或csv文件。然而,我的嵌套循环似乎只从第一个服务部分提取这三个属性,并重复打印它们 示例输入将有多个设备块: 期望输出: 实际产量: 因此,代码应该显示每个设备的每个服务的服务端口、名称和协议列表。但是,当前代

我正在尝试编写一个ruby脚本,以导出Nexpose简单XML结果,对其进行解析,并将所需结果以更漂亮的格式输出,以便于查看。我正在使用Nokogiri解析XML。我的问题是,我有一个嵌套循环,用于每个设备,迭代每个服务部分,并从每个服务部分提取名称、端口和协议属性。最终将打印回文本文件或csv文件。然而,我的嵌套循环似乎只从第一个服务部分提取这三个属性,并重复打印它们

示例输入将有多个设备块:

期望输出:

实际产量:

因此,代码应该显示每个设备的每个服务的服务端口、名称和协议列表。但是,当前代码似乎只是一遍又一遍地打印第一个服务的集合,即123、NTP和udp


我的循环逻辑中是否遗漏了什么?或者你觉得这些循环有什么问题吗?任何有助于这项工作将是有益的。谢谢。

请注意XPath构造//意味着在文档中的任何位置查找元素。您不希望在内部循环中执行此操作,因为您已经为设备执行了此操作

更新

根据新的输入文档,这里有一种提取所需信息的方法。我冒昧地使用了CSV,这是一个很好的Excel输出文件。请注意,只有一个解析循环。代码:

require 'nokogiri'
require 'csv'

doc = Nokogiri::XML(open('report.xml').read)

CSV.open("devices.csv", "wb") do |csv|
  csv << ["Device", "Service", "Port", "Protocol"]
  doc.search('//service').each do |s|
    device = s.xpath('ancestor::device[1]/@address')
    name = s.attr('name')
    port = s.attr('port')
    protocol = s.attr('protocol')
    csv << [device, name, port, protocol]
  end
end

我做了您建议的更改,并在设备循环中添加了一行以放置设备地址。因此,当前输出应该是一个IP地址,然后是所有端口的丑陋列表,然后是下一个IP地址,依此类推。好吧,它至少为额外的服务提供了信息。但是,它看起来像是在打印第一个IP地址,然后是所有设备的所有服务,然后是第二个IP地址,然后是所有设备的所有服务,冲洗并重复。当服务循环在设备循环中迭代时,不应该将其约束到单个设备部分吗?在您的示例代码中,您似乎没有将设备用于任何用途。您可以删除它。我已经更新了我的答案。应该使用它来迭代输入xml文件中的多个设备部分。因此,想象一下上面提供的示例输入对于多个不同的主机重复多次。因此,我希望我的循环至少应该遍历每个设备部分,并提取该部分的所有服务,然后转到下一个设备。相反,我现在得到的是设备A的地址,然后是每个设备的服务列表,设备B的地址,然后是每个设备的服务列表,一直到列表的下方。我更新了我的Ruby代码部分,以反映当前的代码。你犯了同样的错误。您正在使用//服务请求所有服务,更新后,它现在是一个内部循环。正如我所说,您不希望//位于内环中,因为对于外环中的每个设备,您都在请求内环中的所有服务。换句话说,您总共应该只有一个/。解决了这个问题,它会起作用,但不是最优的。我的第二点是,您不需要迭代。您需要服务信息,因此您可以只查找所有服务,无论它们位于多个设备中。
#! /usr/bin/env ruby

require 'rubygems'
require 'nokogiri'

doc = Nokogiri::XML(open('report.xml').read)
device = doc.xpath('//device')

device.each do |d|
 service = d.xpath('//service')
 puts d.attr('address')

 service.each do |s|
   name = s.attr('name')
   port = s.attr('port')
   protocol = s.attr('protocol')

   puts port
   puts protocol
   puts name
 end
end
10.x.x.1
123
udp
NTP
8080
tcp
HTTP

10.x.x.2
53
udp
DNS
80
tcp
HTTP
123
NTP
udp
123
NTP
udp
require 'nokogiri'
require 'csv'

doc = Nokogiri::XML(open('report.xml').read)

CSV.open("devices.csv", "wb") do |csv|
  csv << ["Device", "Service", "Port", "Protocol"]
  doc.search('//service').each do |s|
    device = s.xpath('ancestor::device[1]/@address')
    name = s.attr('name')
    port = s.attr('port')
    protocol = s.attr('protocol')
    csv << [device, name, port, protocol]
  end
end
Device,Service,Port,Protocol
10.x.x.1,NTP,123,udp
10.x.x.1,HTTP,8080,tcp
10.x.x.2,DNS,53,udp
10.x.x.2,HTTP,80,tcp