Posts RSS Comments RSS 253 Posts and 408 Comments till now

More Powershell RegEx fun (playing with ‘route Print’)

A problem come that required me to check the routing table on a large number of machines. My first thought was to try to find a .NET class to dump the table, but alas I could not find any class to do what I wanted. That was a bummer but not unexpected so I decided to just use ol’faithful psexec.exe (Found here.)

Now, for those of you that have used ‘route print’ before you know the output is well… verbose :) and clearly that would not be very helpful for filtering.


Enter ‘Get-RouteTable” (found below)

I share this, not so much because I found it incredibly useful (although I did,) but because it illustrates what I have found to be a great way of creating complex Regular Expressions (for the sake of brevity I will use RegEX for the rest of the article.)

My approach is quiet simple. Create a string object for the RegEx and build the Regex over time testing each section.

Lets take this string for example

Active Routes:
Network Destination        Netmask                 Gateway          Interface            Metric
          0.0.0.0                   0.0.0.0                    192.168.1.1     192.168.1.13     25
        127.0.0.0                 255.0.0.0                127.0.0.1          127.0.0.1           306
        127.0.0.1                 255.255.255.255    127.0.0.1           127.0.0.1           306

I wanted to extract the Network Destination, Netmask, Gateway, Interface, and Metric and make them properties on a custom object. How does one go about that? One way is to create a regular expression with labels and use $matches to extract the information. This is route I took, but instead of trying to write a single complex RegEx and then trouble-shooting it for hours I decided to break down the regular expression into bite size chunks.

To show you what I mean by complex… here is the RegEx you end up with

“^\s+(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<Gateway>\d{1,3
}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<Interface>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<Metric>\d{1,4})$”

As you can this is fairly complex and could be a HUGE pain to trouble shoot.

Following my approach you break down. First you add the begin and end to the regex

$DiscoverRoute  = “^\s+”

$DiscoverRoute += “.+$”

The next step is we add a label and pattern for the “Network Destination” and test

$DiscoverRoute  = “^\s+”

$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”

$DiscoverRoute += “.+$”

(route print) | ?{$_ -match $DiscoverRoute}

If you get results you expect the next step is to make sure $matches sees the labels as you expect

(route print) | ?{$_ -match $DiscoverRoute} | %{$matches}

Great! Now we can the next part

$DiscoverRoute  = “^\s+”
$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “.+$”

Repeat the Last test but make sure you have a value for both Network and NetMask

(route print) | ?{$_ -match $DiscoverRoute} | %{$matches}

Rinse and repeat until you end up with this

$DiscoverRoute  = “^\s+”
$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Gateway>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Interface>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Metric>\d{1,4})”
$DiscoverRoute += “.+$”

This does not work just yet… can you spot why? The reason is because we have been adding “.+$” (zero or more of anything) to the end every time for testing purposes. We now need to remove this and add the $ to the end of the Metric string because the EOL is immediately after the last digit in Metric column. We end up with this

$DiscoverRoute  = “^\s+”
$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Gateway>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Interface>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Metric>\d{1,4})$”

We took a complex RegEx and with a little string concantination we were able to break it down and make the task quite simple.

Note: Another cool effect of this method is that we can easily comment out a section that maybe break the entire regular expression like this

$DiscoverRoute  = “^\s+”
$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
#$DiscoverRoute += “(?<Gateway>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “.+(?<Interface>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Metric>\d{1,4})$”

Just make sure that you add the “.+” to the line just after the comment line. This allows the regular expression to replace that section with basically anything.

NOTE: A final, slightly embarrassing, note. After writing the script below I realized this information (save the persistent routes) is readily available using WMI via the Win32_IP4RouteTable class.

The Code:


Param($Servers)

function Convert-RouteTabletoObject ($data)
{
    # RegEx for Discovery Routes
    $DiscoverRoute  = "^\s+"
    $DiscoverRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $DiscoverRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $DiscoverRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $DiscoverRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $DiscoverRoute += "(?\d{1,4})"
    $DiscoverRoute += "$"

    # RegEx for Persistent Routes
    $PersistentRoute  = "^\s+"
    $PersistentRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $PersistentRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $PersistentRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $PersistentRoute += "(?\d{1,4})"
    $PersistentRoute += "$"

    $Routes = @()
    switch -regex ($data)
    {
        $DiscoverRoute
                    {
                        $NewRoute = "" |Select-Object Network,Netmask,Gateway,Interface,Metric,Persistent
                        $NewRoute.Network = $matches.Network
                        $NewRoute.Netmask = $matches.Netmask
                        $NewRoute.Gateway = $matches.Gateway
                        $NewRoute.Interface = $matches.Interface
                        $NewRoute.Metric = $matches.Metric
                        $NewRoute.Persistent = $False
                        $Routes += $NewRoute
                    }
        $PersistentRoute
                    {
                        $NewRoute = "" |Select-Object Network,Netmask,Gateway,Interface,Metric,Persistent
                        $NewRoute.Network = $matches.Network
                        $NewRoute.Netmask = $matches.Netmask
                        $NewRoute.Gateway = $matches.Gateway
                        $NewRoute.Interface = "N/A"
                        $NewRoute.Metric = $matches.Metric
                        $NewRoute.Persistent = $True
                        $Routes += $NewRoute
                    }
    }
    $Routes
}

foreach($server in $servers)
{
    $myobj = "" | Select-Object Name,RouteTable
    $myobj.Name = $Server
    $myobj.RouteTable = Convert-RouteTabletoObject (invoke-Expression "psexec \\$server route print" 2&gt;$NULL)
    $myobj
}

# Note: You can use WMI to get most of this info – Get-WMIObject Win32_IP4RouteTable -ComputerName

Trackback this post | Feed on Comments to this post

Leave a Reply

You must be logged in to post a comment.