如何进行多行匹配并将其分配给哈希?

时间:2014-11-03 19:41:02

标签: ruby-on-rails ruby regex

我正在从net::ssh会话输出到ASA并尝试为每个vpn用户构建一个哈希值,然后将每个哈希值添加到一个数组中。

以下是连接一个用户时的输出。如果有多个用户,则每个用户有更多这样的行:

Username:arozar
Index:654
AssignedIP:10.254.254.7
PublicIP:4.2.2.2
Protocol:AnyConnect-ParentSSL-TunnelDTLS-Tunnel
License:AnyConnectEssentials
Encryption:AnyConnect-Parent:(1)noneSSL-Tunnel:(1)AES256DTLS-Tunnel:(1)AES256
Hashing:AnyConnect-Parent:(1)noneSSL-Tunnel:(1)SHA1DTLS-Tunnel:(1)SHA1
BytesTx:6104355
BytesRx:1191505
GroupPolicy:vpn
TunnelGroup:anyconnect
LoginTime:08:51:55ESTMonNov32014
Duration:1h:08m:48s
Inactivity:0h:00m:00s
NACResult:Unknown

VLANMapping:N/A
VLAN:none

以下是我正在处理的代码:

  @ac_array = Array.new
  session_output = cmd_session.cmd('show vpn-sessiondb anyconnect')
  cmd_session.close
  session_output.each_line do |line|
    username = line.match(/(?<=Username:)(\w+\S+)/)
    assigned_ip = line.match(/(?<=AssignedIP:)(\w+\S+)/)
    public_ip = line.match(/(?<=PublicIP:)(\w+\S+)/)
    license = line.match(/(?<=License:)(\w+\S+)/)
    bytestx = line.match(/(?<=BytesTx:)(\w+\S+)/)
    bytesrx = line.match(/(?<=BytesRx:)(\w+\S+)/)
    group_policy = line.match(/(?<=GroupPolicy:)(\w+\S+)/)
    tunnel_group = line.match(/(?<=TunnelGroup:)(\w+\S+)/)

要将这些匹配放入哈希,我必须这样做:

    unless username.nil?
      anyconnect_hash = {username:username[0]}
      @ac_array.push(anyconnect_hash)
    end
    unless assigned_ip.nil?
       assigned_ip_hash = {assigned_ip:assigned_ip[0]}
      @ac_array.push(assigned_ip_hash)
    end
    unless public_ip.nil?
      public_ip_hash = {public_ip:public_ip[0]}
      @ac_array.push(public_ip_hash)
    end

    AND SO ONE DOWN THE LINE for all the matches.....

但我不认为这是最好的方式,在我看来也不是真的有效。我喜欢使用一行unless语句,并从那里将所有匹配项分配给哈希。

以下是我认为会更好的东西:

    unless username.nil? and assigned_ip.nil? and public_ip.nil?
      anyconnect_hash = {username:username[0], assigned_ip:assigned_ip[0], public_ip:public_ip[0]}
      @ac_array.push(anyconnect_hash)
    end 

不幸的是,通过这个设置,我得到了“未定义的方法`[]'为nil:NilClass”。

2 个答案:

答案 0 :(得分:4)

您可以为每个用户执行以下操作。

<强>代码

ATTRIBUTES = [:Username, :AssignedIP, :PublicIP, :License,
              :BytesTx, :BytesRx, :GroupPolicy, :TunnelGroup]

def hashify(text)
  text.each_line.with_object({}) do |line, h|
    ATTRIBUTES.each do |sym|
      v = line[/(?<=#{sym.to_s}:)\w+\S+/]
      h[sym] = v if v
    end
  end
end  

示例

text =
"Username:arozar
AssignedIP:10.254.254.7
PublicIP:4.2.2.2
License:AnyConnectEssentials
BytesTx:6104355
BytesRx:1191505
GroupPolicy:vpn
TunnelGroup:anyconnect"

hashify(text)
  #=> {:Username=>"arozar", :AssignedIP=>"10.254.254.7", :PublicIP=>"4.2.2.2",
  #    :License=>"AnyConnectEssentials", :BytesTx=>"6104355",
  #    :BytesRx=>"1191505", :GroupPolicy=>"vpn", :TunnelGroup=>"anyconnect"}

备注

  • 如果你真的想要一个哈希数组,那么转换哈希值(hashify(text).to_a.map {|k,v| {k=>v} })很容易,但考虑一下你是否会用散列更好。这将为您提供每个用户一个哈希的数组。我怀疑,更好的方法是为每个用户提供一个带有一个元素的哈希值。对于示例中的用户,该键值对将是:

"arozar" => { :AssignedIP=>"10.254.254.7", :PublicIP=>"4.2.2.2",
              :License=>"AnyConnectEssentials", :BytesTx=>"6104355",
              :BytesRx=>"1191505", :GroupPolicy=>"vpn",
              :TunnelGroup=>"anyconnect" }
  • 如果示例中显示的哈希值不是您想要的,则需要在正则表达式中修改\w+\S+ [此处与(\w+\S+)相同]。

    < / LI>
  • 这使用方法String#[]的形式,将正则表达式作为参数。

  • 如果你更喜欢,而不是使用正则表达式,你可以写k,v = line.split(':'); u = v[/\w+\S+/]; h[sym] = u if u,但我更喜欢它。

答案 1 :(得分:3)

好吧,我从以下开始:

text = 'Username:arozar
Index:654
AssignedIP:10.254.254.7
PublicIP:4.2.2.2
Protocol:AnyConnect-ParentSSL-TunnelDTLS-Tunnel
License:AnyConnectEssentials
Encryption:AnyConnect-Parent:(1)noneSSL-Tunnel:(1)AES256DTLS-Tunnel:(1)AES256
Hashing:AnyConnect-Parent:(1)noneSSL-Tunnel:(1)SHA1DTLS-Tunnel:(1)SHA1
BytesTx:6104355
BytesRx:1191505
GroupPolicy:vpn
TunnelGroup:anyconnect
LoginTime:08:51:55ESTMonNov32014
Duration:1h:08m:48s
Inactivity:0h:00m:00s
NACResult:Unknown

VLANMapping:N/A
VLAN:none
'

text.split("\n").reject(&:empty?).map{ |l| l.split(':', 2) }.to_h
# => {"Username"=>"arozar",
#     "Index"=>"654",
#     "AssignedIP"=>"10.254.254.7",
#     "PublicIP"=>"4.2.2.2",
#     "Protocol"=>"AnyConnect-ParentSSL-TunnelDTLS-Tunnel",
#     "License"=>"AnyConnectEssentials",
#     "Encryption"=>
#      "AnyConnect-Parent:(1)noneSSL-Tunnel:(1)AES256DTLS-Tunnel:(1)AES256",
#     "Hashing"=>
#      "AnyConnect-Parent:(1)noneSSL-Tunnel:(1)SHA1DTLS-Tunnel:(1)SHA1",
#     "BytesTx"=>"6104355",
#     "BytesRx"=>"1191505",
#     "GroupPolicy"=>"vpn",
#     "TunnelGroup"=>"anyconnect",
#     "LoginTime"=>"08:51:55ESTMonNov32014",
#     "Duration"=>"1h:08m:48s",
#     "Inactivity"=>"0h:00m:00s",
#     "NACResult"=>"Unknown",
#     "VLANMapping"=>"N/A",
#     "VLAN"=>"none"}

如果您的Ruby版本不支持to_h,请将命令包装在Hash[...]中。

将其附加到数组以使用以下内容聚合所有创建的哈希值:

user_hashes = []
users.each do |user|
  user_hashes << user.split("\n").reject(&:empty?).map{ |l| l.split(':', 2) }.to_h
end

如果没有显示两个用户而不是一个用户的日志示例,我就无法分解用户如何拆分输出,但这应该非常简单。猜测格式:

users = 'Username:arozar
Index:654
AssignedIP:10.254.254.7
PublicIP:4.2.2.2
Protocol:AnyConnect-ParentSSL-TunnelDTLS-Tunnel
License:AnyConnectEssentials
Encryption:AnyConnect-Parent:(1)noneSSL-Tunnel:(1)AES256DTLS-Tunnel:(1)AES256
Hashing:AnyConnect-Parent:(1)noneSSL-Tunnel:(1)SHA1DTLS-Tunnel:(1)SHA1
BytesTx:6104355
BytesRx:1191505
GroupPolicy:vpn
TunnelGroup:anyconnect
LoginTime:08:51:55ESTMonNov32014
Duration:1h:08m:48s
Inactivity:0h:00m:00s
NACResult:Unknown

VLANMapping:N/A
VLAN:none
Username:popeye
Index:655
AssignedIP:10.254.254.8
PublicIP:4.2.2.3
Protocol:AnyConnect-ParentSSL-TunnelDTLS-Tunnel
License:AnyConnectEssentials
Encryption:AnyConnect-Parent:(1)noneSSL-Tunnel:(1)AES256DTLS-Tunnel:(1)AES256
Hashing:AnyConnect-Parent:(1)noneSSL-Tunnel:(1)SHA1DTLS-Tunnel:(1)SHA1
BytesTx:6104355
BytesRx:1191505
GroupPolicy:vpn
TunnelGroup:anyconnect
LoginTime:09:52:56estmonnov32014
Duration:1h:08m:47s
Inactivity:0h:00m:00s
NACResult:Unknown

VLANMapping:N/A
VLAN:none
'

这是将其分解为哈希数组的代码:

user_hashes = []
users.split("\n").reject(&:empty?).slice_before(/^Username/).each do |user|
  user_hashes << user.map{ |l| l.split(':', 2) }.to_h
end

user_hashes看起来像什么:

user_hashes 
# => [{"Username"=>"arozar",
#      "Index"=>"654",
#      "AssignedIP"=>"10.254.254.7",
#      "PublicIP"=>"4.2.2.2",
#      "Protocol"=>"AnyConnect-ParentSSL-TunnelDTLS-Tunnel",
#      "License"=>"AnyConnectEssentials",
#      "Encryption"=>
#       "AnyConnect-Parent:(1)noneSSL-Tunnel:(1)AES256DTLS-Tunnel:(1)AES256",
#      "Hashing"=>
#       "AnyConnect-Parent:(1)noneSSL-Tunnel:(1)SHA1DTLS-Tunnel:(1)SHA1",
#      "BytesTx"=>"6104355",
#      "BytesRx"=>"1191505",
#      "GroupPolicy"=>"vpn",
#      "TunnelGroup"=>"anyconnect",
#      "LoginTime"=>"08:51:55ESTMonNov32014",
#      "Duration"=>"1h:08m:48s",
#      "Inactivity"=>"0h:00m:00s",
#      "NACResult"=>"Unknown",
#      "VLANMapping"=>"N/A",
#      "VLAN"=>"none"},
#     {"Username"=>"popeye",
#      "Index"=>"655",
#      "AssignedIP"=>"10.254.254.8",
#      "PublicIP"=>"4.2.2.3",
#      "Protocol"=>"AnyConnect-ParentSSL-TunnelDTLS-Tunnel",
#      "License"=>"AnyConnectEssentials",
#      "Encryption"=>
#       "AnyConnect-Parent:(1)noneSSL-Tunnel:(1)AES256DTLS-Tunnel:(1)AES256",
#      "Hashing"=>
#       "AnyConnect-Parent:(1)noneSSL-Tunnel:(1)SHA1DTLS-Tunnel:(1)SHA1",
#      "BytesTx"=>"6104355",
#      "BytesRx"=>"1191505",
#      "GroupPolicy"=>"vpn",
#      "TunnelGroup"=>"anyconnect",
#      "LoginTime"=>"09:52:56estmonnov32014",
#      "Duration"=>"1h:08m:47s",
#      "Inactivity"=>"0h:00m:00s",
#      "NACResult"=>"Unknown",
#      "VLANMapping"=>"N/A",
#      "VLAN"=>"none"}]

slice_before是一种很好的方法,它采用一种模式来标记应该开始新子数组的行。

另外,请将此作为示例代码中前三行的替代方法进行冥想:

ac_array = []
`show vpn-sessiondb anyconnect`.split("\n").reject(&:empty?).map{ |l| l.split(':', 2) }.to_h

这仅适用于上面针对单个用户显示的输出。