Question

I am working on a script to parse out certain values from a file where configuration data is spread across multiple lines and have been having a lot of trouble doing it in Ruby. The data is shown below. The piece that ties data together is the Unit number. What I need to do is grab the Unit, AdminIPAddress, InterfaceDisplayString, and Name values from this data and save it in such a way that it makes sense. To me, a multi-dimensional Perl hash would be perfect, but the syntax is beyond my understanding of the Ruby language.

Any help you can provide would be greatly appreciated. I'd like the data to be in a hash like the following:

{Unit0}  => {IPAddress => 10.5.52.60, Name => 440_6PT_6AS_inf4, Port = 10_10},
{Unit1}  => ...

I have been able to create an array of all of the important lines by doing the following:

if (line =~ /WaTestProfile,Unit[\d+]/)
  # Manipulate the data first to remove pesky special characters
  result = line.gsub(/[^0-9A-Za-z,\.\,\{\}\_ ]/, '')
  if result !~ /DDOS|PhysIf/
    temp_line += [ "#{result}" ]
  end
end

Then I can use some regex to pull out the specific fields:

unit_array.sort.each_with_index do |line, idx|
  if result = line.match(/W[a|r]TestProfile,Unit(?:\d+),AdminIPAddress\s+\{(.*)\}/)
    temp = result.captures
    ip_array += [ temp ]
    printf "I captured #{temp}\n"
  end
  if result = line.match(/W[a|r]TestProfile,Unit(?:\d+),Interface,0,InterfaceDisplayString\s+\{(.*)\}/)
    temp = result.captures
    port_array += [ temp]
    printf "I have captured #{temp}\n"
  end
  if result = line.match(/W[a|r]TestProfile,Unit(?:\d+),Interface,0,Name\s+\{(.*)\}/)
    temp = result.captures
    name_array += [ temp ]
    printf "Finally, I captured #{temp}\n"
  end
end

This puts everything into three separate arrays: top down from Unit 0 to 5, but I can't take it to the next step and stick those in a multi-dimensional array and I think the above code could also be improved.

WaTestProfile,Unit0,AdminIPAddress                                      {10.5.52.60}\
WaTestProfile,Unit0,Interface,0,DDOSAttacks                             {}\
WaTestProfile,Unit0,Interface,0,DDOSConfig                              {}\
WaTestProfile,Unit0,Interface,0,DDOSEnabled                             {off}\
WaTestProfile,Unit0,Interface,0,InterfaceDisplayString                  {10_10}\
WaTestProfile,Unit0,Interface,0,Name                                    {440_6PT_6AS_inf5}\
WaTestProfile,Unit0,Interface,0,PhysIf                                  {10}\
WaTestProfile,Unit1,AdminIPAddress                                      {10.5.52.60}\
WaTestProfile,Unit1,Interface,0,DDOSAttacks                             {}\
WaTestProfile,Unit1,Interface,0,DDOSConfig                              {}\
WaTestProfile,Unit1,Interface,0,DDOSEnabled                             {off}\
WaTestProfile,Unit1,Interface,0,InterfaceDisplayString                  {8_8}\
WaTestProfile,Unit1,Interface,0,Name                                    {440_6PT_6AS_inf4}\
WaTestProfile,Unit1,Interface,0,PhysIf                                  {8}\
WaTestProfile,Unit2,AdminIPAddress                                      {10.5.52.60}\
WaTestProfile,Unit2,Interface,0,DDOSAttacks                             {}\
WaTestProfile,Unit2,Interface,0,DDOSConfig                              {}\
WaTestProfile,Unit2,Interface,0,DDOSEnabled                             {off}\
WaTestProfile,Unit2,Interface,0,InterfaceDisplayString                  {2_2}\
WaTestProfile,Unit2,Interface,0,Name                                    {440_6PT_6AS_inf1}\
WaTestProfile,Unit2,Interface,0,PhysIf                                  {2}\
WaTestProfile,Unit3,AdminIPAddress                                      {10.5.52.60}\
WaTestProfile,Unit3,Interface,0,DDOSAttacks                             {}\
WaTestProfile,Unit3,Interface,0,DDOSConfig                              {}\
WaTestProfile,Unit3,Interface,0,DDOSEnabled                             {off}\
WaTestProfile,Unit3,Interface,0,InterfaceDisplayString                  {4_4}\
WaTestProfile,Unit3,Interface,0,Name                                    {440_6PT_6AS_inf2}\
WaTestProfile,Unit3,Interface,0,PhysIf                                  {4}\
WaTestProfile,Unit4,AdminIPAddress                                      {10.5.52.60}\
WaTestProfile,Unit4,Interface,0,DDOSAttacks                             {}\
WaTestProfile,Unit4,Interface,0,DDOSConfig                              {}\
WaTestProfile,Unit4,Interface,0,DDOSEnabled                             {off}\
WaTestProfile,Unit4,Interface,0,InterfaceDisplayString                  {6_6}\
WaTestProfile,Unit4,Interface,0,Name                                    {440_6PT_6AS_inf3}\
WaTestProfile,Unit4,Interface,0,PhysIf                                  {6}\
WaTestProfile,Unit5,AdminIPAddress                                      {10.5.52.60}\
WaTestProfile,Unit5,Interface,0,DDOSAttacks                             {}\
WaTestProfile,Unit5,Interface,0,DDOSConfig                              {}\
WaTestProfile,Unit5,Interface,0,DDOSEnabled                             {off}\
WaTestProfile,Unit5,Interface,0,InterfaceDisplayString                  {0_0}\
WaTestProfile,Unit5,Interface,0,Name                                    {440_6PT_6AS_inf0}\
WaTestProfile,Unit5,Interface,0,PhysIf                                  {0}\
Was it helpful?

Solution

So I think this is probably what you are looking for though it was kind of hard to understand the question. Here's my solution and I'll go over each part.

hsh = Hash.new()

File.open('config.txt', 'r').each_line do |line|
  match =
  /WaTestProfile,
    (?<unit>Unit\d+)  # match the unit and unit number
    ,.*               # match the comma and all the way up to the property
    (?<property>AdminIPAddress|InterfaceDisplayString|Name) # match the property
    \s+               # match all spaces up to the property value
    {(?<property_value>.*)} # match the property value
  /x =~ line

  if match
    hsh[unit.to_sym] ||= {}
    hsh[unit.to_sym][property.to_sym] = property_value
  end
end

hsh.each do |key, value|
  puts key.to_s + value.to_s
end

So this is just creating a hash, we also could have done hsh = {}

hsh = Hash.new()

We then open the file for reading and read each line in. On each line we attempt to find a match. I've used a more advanced form of regex here using named captures and ignoring whitespace because it's a little more complicated, so it makes it easier to read

  match =
  /WaTestProfile,
    (?<unit>Unit\d+)  # match the unit and unit number
    ,.*               # match the comma and all the way up to the property
    (?<property>AdminIPAddress|InterfaceDisplayString|Name|PhysIf) # match the property
    \s+               # match all spaces up to the property value
    {(?<property_value>.*)} # match the property value
  /x =~ line

Now I can reference the variable unit and get the entire unit and unit number.
We also use the match = at the start so that we can remove the DDOS lines from the equation.


Finally we check if there was a match. If there was we either create a new key corresponding to a hash in the top level hash or we grab an existing one. Then we set the property to the correct property value. I assumed since you didn't put quotes around the keys that you wanted them to be symbols, which is probably a better idea anyway.

  if match
    hsh[unit.to_sym] ||= {}
    hsh[unit.to_sym][property.to_sym] = property_value
  end

The final output looks like this:

Unit0{:AdminIPAddress=>"10.5.52.60", :InterfaceDisplayString=>"10_10", :Name=>"440_6PT_6AS_inf5"}
Unit1{:AdminIPAddress=>"10.5.52.60", :InterfaceDisplayString=>"8_8", :Name=>"440_6PT_6AS_inf4"}
Unit2{:AdminIPAddress=>"10.5.52.60", :InterfaceDisplayString=>"2_2", :Name=>"440_6PT_6AS_inf1"}
Unit3{:AdminIPAddress=>"10.5.52.60", :InterfaceDisplayString=>"4_4", :Name=>"440_6PT_6AS_inf2"}
Unit4{:AdminIPAddress=>"10.5.52.60", :InterfaceDisplayString=>"6_6", :Name=>"440_6PT_6AS_inf3"}
Unit5{:AdminIPAddress=>"10.5.52.60", :InterfaceDisplayString=>"0_0", :Name=>"440_6PT_6AS_inf0"}

All contained in the hash hsh

Psst:
If you want a shorter version, here it is:

hsh = {}

File.open('config.txt', 'r').each_line do |line|
  if /WaTestProfile,(?<unit>Unit\d+),.*(?<property>AdminIPAddress|InterfaceDisplayString|Name)\s+{(?<property_value>.*)}/x =~ line
    hsh[unit.to_sym] ||= {}
    hsh[unit.to_sym][property.to_sym] = property_value
  end
end

p hsh

Output:

{:Unit0=>{:AdminIPAddress=>"10.5.52.60", :InterfaceDisplayString=>"10_10", :Name=>"440_6PT_6AS_inf5"}, :Unit1=>{:AdminIPAddress=>"10.5.52.60", :InterfaceDisplayString=>"8_8", :Name=>"440_6PT_6AS_inf4"}, :Unit2=>{:AdminIPAddress=>"10.5.52.60", :InterfaceDisplayString=>"2_2", :Name=>"440_6PT_6AS_inf1"}, :Unit3=>{:AdminIPAddress=>"10.5.52.60", :InterfaceDisplayString=>"4_4", :Name=>"440_6PT_6AS_inf2"}, :Unit4=>{:AdminIPAddress=>"10.5.52.60", :InterfaceDisplayString=>"6_6", :Name=>"440_6PT_6AS_inf3"}, :Unit5=>{:AdminIPAddress=>"10.5.52.60", :InterfaceDisplayString=>"0_0", :Name=>"440_6PT_6AS_inf0"}}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top