Posts RSS Comments RSS 253 Posts and 411 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.