我有一个包含以下字符串的response
变量:
response
变量(这个输出是我从telnet会话中提取的,为了简化起见,我没有在下面的代码中显示):
mydummyhost# show ip bgp 43.245.43.105
BGP routing table entry for 43.245.43.0/24
Paths: (2 available, best #1, table Default-IP-Routing-Table)
Not advertised to any peer
38561 2914 55432, (aggregated by 55532 202.68.67.134)
202.158.215.44 from 201.158.202.44 (202.158.215.62)
Community: 7575:1002 7575:2462 7575:3002 7575:6001 7575:8001
Last update: Tue Sep 22 12:25:17 2020
38561 2914 55433, (aggregated by 55433 202.68.67.135)
202.158.215.52 from 202.158.215.52 (202.158.215.62)
Community: 7575:1002 7575:2462 7575:3002 7575:6001 7575:8001
Last update: Mon Sep 21 06:44:58 2020
我有一段代码,我正在使用它来尝试遍历上面的字符串行,基本上得到以下结果:
所需结果:
43.245.43.105 is domestic peering (On-Net) originated by AS 55432 via path 38561 2914 55432
43.245.43.105 is domestic peering (On-Net) originated by AS 55433 via path 38561 2914 55433
法典:
#!/usr/bin/env ruby
require 'net/telnet'
ipaddress = "43.245.43.105"
cat = []
response = ""
origin = []
paths = []
net = []
community = []
onoffnet= {
ond:"domestic (On-Net)",
oni:"international research (On-Net)",
opd:"domestic peering (On-Net)",
ofd:"domestic transit (Off-Net)",
opi:"international peering (Off-Net)",
ofi:"international transit (Off-Net)"
}
response.each_line do |line|
if line =~ /BGP routing table entry for (d+.d+.d+.d+.*)/
net[i] = $1
elsif line =~ /Community: (.*)$/
community[i] = $1
elsif line =~ /^s+([0-9 ]+),.*/
paths, aggregatedBy = line.split(", ")
paths[i] = paths.strip
origin[i] == aggregatedBy.split(" ")[2]
elsif line =~ /Last update:/
i += 1
end
end
if i == 0
print "ERROR, no data found for the IP."
else
i = 0
net.each do | ip |
if community[i] =~ /7575:1000/
cat[i] = onoffnet.fetch(:ond)
elsif community[i] =~ /7575:1001/
cat[i] = onoffnet.fetch(:oni)
elsif community[i] =~ /7575:1002/
if community[i] =~ /7575:6001/
cat[i] = onoffnet.fetch(:opd)
else
cat[i] = onoffnet.fetch(:opi)
end
elsif community[i] =~ /7575:1003/
if community[i] =~ /7575:6001/
cat[i] = onoffnet.fetch(:ofd)
else
cat[i] = onoffnet.fetch(:ofi)
end
end
i += 1
if origin[i].to_s.length > 0 && paths[i].to_s.length > 0
puts "#{ipaddress} is cat[i] network ip[i] originated by AS #{origin} via path #{paths} ."
else
puts #{ipaddress} + "is" + cat[i] + "network" + ip[i] + "n"
puts "Test"
end
end
end
当我运行这个时,只有"测试"显示为输出:
[root@mydummyhost]# ./telnet.rb
Test
基本上,我正在尝试使用line =~ /Last update:/
来确定我有另一个单独的路径和原点要通过递增i
来显示,如果这有意义的话。
所以我知道它已经达到了if
条件,但是我不确定为什么它没有显示第一个打印行puts #{ipaddress} + "is" + cat[i] + "network" + ip[i] + "n"
,这似乎是空的。
编辑:
将puts #{ipaddress} + "is" + cat[i] + "network" + ip[i] + "n"
更改为puts "#{ipaddress} is #{cat[i]} network #{ip[i]}n"
后(如@trueunlessfalse建议的,谢谢!),我现在可以看到一个输出:
[root@dummyhost]# ./telnet.rb
43.245.43.105 is network 3
不幸的是,这甚至没有接近我正在寻找的所需结果。
我知道问题出在我迭代该response
var 并填充数组的方式上 - 我有一个有效的 perl 代码并试图将其转换为 ruby - ,我只是不确定如何解决这个问题或什么是更好的方法来完成该输出。
任何建议我应该如何迭代response
以便可以根据Last update:
标记的"块"末尾递增的i
值填充数组?
谢谢 J
预期结果似乎取决于给定字符串中包含的以下类型的值:
- BGP ip(例如,
"43.245.43.105"
) - 路径值(例如,
"38561 2914 55432"
) - 原始值(例如,
"38561"
) - 社区价值观(例如,
"7575:1002 7575:2462 7575:3002 7575:6001 7575:8001"
)
我建议您首先专注于提取这些值,然后构造所需的字符串相当简单。我的回答仅限于这个初始任务。
为了产生一些数字,我将首先构造您的字符串response
。
response =<<~BITTER_END
mydummyhost# show ip bgp 43.245.43.105
BGP routing table entry for 43.245.43.0/24
Paths: (2 available, best #1, table Default-IP-Routing-Table)
Not advertised to any peer
38561 2914 55432, (aggregated by 55532 202.68.67.134)
202.158.215.44 from 201.158.202.44 (202.158.215.62)
Community: 7575:1002 7575:2462 7575:3002 7575:6001 7575:8001
Last update: Tue Sep 22 12:25:17 2020
38561 2914 55433, (aggregated by 55433 202.68.67.135)
202.158.215.52 from 202.158.215.52 (202.158.215.62)
Community: 7575:1002 7575:2462 7575:3002 7575:6001 7575:8001
Last update: Mon Sep 21 06:44:58 2020
BITTER_END
我对Telnet了解不多,但它似乎包含response
数据块,该块以"mydummyhost"开头的行开头。我写这个是为了允许多个这样的块(每个块都以一行开头,'mydummyhost'),所以作为第一步,我将应用带有正则表达式的 String#scan,如下所示。
arr = response.scan(/^mydummyhostD+.+?(?=z|^mydummyhost)/m)
#=> ["mydummyhost# show ip bgp 43.245.43.105nBGP routing table entry for 43.245.43.0/24nPaths: (2 available, best #1, table Default-IP-Routing-Table)n Not advertised to any peern 38561 2914 55432, (aggregated by 55532 202.68.67.134)n 202.158.215.44 from 201.158.202.44 (202.158.215.62)n Community: 7575:1002 7575:2462 7575:3002 7575:6001 7575:8001n Last update: Tue Sep 22 12:25:17 2020nn 38561 2914 55433, (aggregated by 55433 202.68.67.135)n 202.158.215.52 from 202.158.215.52 (202.158.215.62)n Community: 7575:1002 7575:2462 7575:3002 7575:6001 7575:8001n Last update: Mon Sep 21 06:44:58 2020n"]
我们可以在自由间距模式下编写正则表达式以使其自我记录。
/
^mydummyhostD+ # match 'mydummyhost' at the beginning of a line followed
# by 1+ characters other than digits (D)
.+ # match 1+ characters, including line terminators
? # make previous match lazy (aka non-greedy)
(?= # begin a positive lookahead
z # match end of string
| # or
^mydummyhost # match '^mydummyhost' at the beginning of a line
) # end positive lookahead
/mx # specify multiline (m) and free-spacing regex definition modes
多行模式(其他语言的名称不同)使点匹配行终止符( 和 \r)以及其他字符。
您将在此处看到arr
包含一个元素。下一步是将arr
映射到每个块的值。为了简化表示,我将假设字符串包含一个块,即response
,但应该很明显它如何被推广。
提取 BGP IP
bgp_rgx = /^mydummyhostD+Kd{1,3}(?:.d{1,3}){3}$/
bgp_ip = response[bgp_rgx]
#=> "43.245.43.105"
请参阅字符串#[]。自由间距模式下的正则表达式:
bgp_rgx =
/
^mydummyhostD+ # match 'mydummyhost' at the begining of a line (^),
# followed by 1+ characters other than digits (D)
K # reset the beginning of the match to the current location
# and discard any previously-matched characters from the
# match that is returned
d{1,3} # match 1-3 digits
(?:.d{1,3}) # match '.' followed by 1-3 characters, save to a
# non-capture group
{3} # execute the foregoing non-capture group 3 times
$ # match end of line
/x # specify free-spacing regex definition mode
提取路径值
path_rgx = /(?<=^ {2})d+(?: +d+){2}(?=,)/
paths = response.scan(path_rgx)
#=> ["38561 2914 55432", "38561 2914 55433"]
在自由间距模式下:
path_rgx =
/
(?<=^[ ]{2}) # use a positive lookbehind (?<=...) to assert that the
# match that follows is preceded by two spaces at the
# beginning of a line
d+ # match 1+ digits
(?:[ ]+d+) # match 1+ spaces followed by 1+ digits, save to non-capture group
{2} # execute the foregoing non-capture group 2 times
(?=,) # use a positive lookahead (?=...) to assert that the
# preceding match is followed by ','
/x # specify free-spacing regex definition mode
请注意,在自由间距模式下编写正则表达式时,在解析表达式之前会删除所有空格。因此,有必要保护所有不应被剥离的空间。我通过将空格字符放在捕获组中([ ]
)中来做到这一点。还有其他方法可以保护空间,但这并不重要。
从路径值中提取原始值
originated = paths.map { |s| s[/d+/] }
#=> ["38561", "38561"]
正则表达式显示"匹配一个或多个数字"。
提取社区价值
community_rgx = /^ {6}Community: +Kd+:d+(?: +d+:d+)+/
community = response.scan(community_rgx)
#=> ["7575:1002 7575:2462 7575:3002 7575:6001 7575:8001",
# "7575:1002 7575:2462 7575:3002 7575:6001 7575:8001"]
在自由间距模式下:
community_rgx =
/
^[ ]{6} # match 6 spaces at beginning of a line
Community:[ ]+ # match 'Community:' followed by 1+ spaces
K # reset the beginning of the match to the current location
# and discard any previously-matched characters from the
d+:d+ # match 1+ digits, ':', 1+ digits
(?:[ ]+d+:d+) # match 1+ spaces, 1+ digits, ':', 1+ digits, save
# to a non-capture group
+ # execute the foregoing non-capture group 1+ times
/x # specify free-spacing regex definition mode
将值合并到哈希中(可选)
params = {
bgp_ip: bgp_ip,
values: originated.zip(paths, community).map do |o,p,c|
{ originated: o, path: p, community: c }
end
}
#=> {:bgp_ip=>"43.245.43.105",
# :values=>[
# {:originated=>"38561", :path=>"38561 2914 55432",
# :community=>"7575:1002 7575:2462 7575:3002 7575:6001 7575:8001"},
# {:originated=>"38561", :path=>"38561 2914 55433",
# :community=>"7575:1002 7575:2462 7575:3002 7575:6001 7575:8001"}
# ]
# }
请参阅数组#zip。
同样,如果字符串包含多个块,则会返回哈希数组,例如params
。
更新:这个答案并没有解决作者代码的所有问题,而只是为什么其中一个puts
没有给出任何输出的问题。
我本来会预料到这一行:
puts #{ipaddress} + "is" + cat[i] + "network" + ip[i] + "n"
抛出错误,因为我以前从未尝试在不先打开字符串的情况下插入字符串。
我在控制台中尝试过这个,实际上它只是打印了一个空行:
irb(main):002:0> a = "foo"
=> "foo"
irb(main):003:0> puts #{a}
=> nil
但是,这按预期工作:
irb(main):004:0> puts "#{a}"
foo
=> nil
尝试将此行重写为:
puts "#{ipaddress} is #{cat[i]} network #{ip[i]}n"