Tip

Applying regular expressions (regex) to Windows PowerShell

It may seem daunting at first, but the combination of regular expressions (regex) with PowerShell can be an admin’s best friend when it comes to certain complex tasks.

The time has come to talk about regular expressions. These wonders of modern text parsing can be one of the most powerful tools an IT guy can have. In simple terms, you can think of regular expressions (lovingly called regex) as a shorthand way to describe text. This is easier to illustrate than describe, so let’s look at an example.

Say you have a complex password policy that requires a password to be at least eight characters long with at least one upper and lowercase letter, and at least one digit or symbol. How would you check for that? It would be quite complicated if you had to check each password manually instead of using a script. But with the power of regex and Windows PowerShell at your beckon call you could simply do this:

$password –match “^.*(?=.{6,})(?=.*[a-z])(?=.*[A-Z])(?=.*[\d\W]).*$”

Now, I know that looks daunting and trust me, I understand. I feared regular expressions for most of my IT life. They always looked so cryptic and complex that I just assumed I would never get it. It wasn’t long after adopting PowerShell, however, that I had a need that simply required regular expressions, so I bit the bullet. To my surprise, it wasn’t nearly as hard as I thought. Don’t get me wrong though, they can become pretty complex and I consider my “regex foo” amateur at best.

So while teaching regular expressions is beyond the scope of this article, general knowledge of it is critical to understanding the rest. If you are unfamiliar with regex you can start by getting a basic understanding. It shouldn’t take too long and it is worth the time.

One final note before we head off to the regex world of PowerShell -- there are many “flavors” of regular expressions and while most are very similar, they do have slight differences. In this case, we are only going to deal with the one used by .NET Framework 4.

Regular expression tools for Windows PowerShell
Here’s a look at some regex commands and examples of how each is used.

Match operator – This matches a string using regular expressions.

Example:
$password –match “^.*(?=.{6,})(?=.*[a-z])(?=.*[A-Z])(?=.*[\d\W]).*$”

$matches – This is created by Switch, -match or -notmatch operators and contains a hash table of any string values that were matched.

System.Text.RegularExpressions.Regex – This is the .NET class for dealing with regular expressions. PowerShell has a built-in shortcut for this [RegEx] so you can create an instance by simply doing the following:

$ComplexPasswordRegEx = [RegEx] “^.*(?=.{6,})(?=.*[a-z])(?=.*[A-Z])(?=.*[\d\W]).*$”

Switch – This is one of the most powerful loop constructs in the PowerShell language (and probably deserves its own article), but for now we are only going to talk about the –regex flag. You can use Switch with the –regex flag to move through an array of strings and perform a scriptblock on each “match”.

Example:
Switch –regex ($MyPasswords)
{
“^.*(?=.{6,})(?=.*[a-z])(?=.*[A-Z])(?=.*[\d\W]).*$” {“Valid Password”}
Default {“Invalid Password”}
}

For more information on Switch use help about_switch.

Putting regex commands together
Finally, here is a PowerShell function I wrote that uses all of the above tools to parse the results of the netstat command and create a PowerShell object that is more “pipeline” friendly.

function Get-NetStat
{

  [Cmdletbinding(DefaultParameterSetName="All")]
  Param(
    [Parameter()]
    [string]$ProcessName,

    [Parameter()]
    [ValidateSet("LISTENING",
    "ESTABLISHED", "CLOSE_WAIT","TIME_WAIT")]
    [string]$State,

    [Parameter(ParameterSetName="Interval")]
    [int]$Interval,

    [Parameter()]
    [int]$Sleep = 1,

    [Parameter(ParameterSetName="Loop")]
    [switch]$Loop
  )

  function Parse-Netstat ($NetStat)
  {
    Write-Verbose " [Parse-Netstat] :: Parsing Netstat
    results"
    switch -regex ($NetStat)
    {
      $RegEx
      {
        Write-Verbose " [Parse-Netstat] :: creating
              Custom object"
        $myobj = @{
          Protocol      = $matches.Protocol
          LocalAddress  = $matches.LocalAddress.split(":")[0]
          LocalPort     = $matches.LocalAddress.split(":")[1]
          RemoteAddress = $matches.RemoteAddress.split(":")[0]
          RemotePort    = $matches.RemoteAddress.split(":")[1]
          State         = $matches.State
          ProcessID     = $matches.PID
          ProcessName   = Get-Process-id $matches.PID -ea 0 |
                          %{$_.name}
            }

        $obj = New-Object PSCustomObject -Property $myobj
        $obj.PSTypeNames.Clear()
        $obj.PSTypeNames.Add('BSonPosh.NetStatInfo')
        Write-Verbose " [Parse-Netstat] :: Created object for
        [$($obj.LocalAddress):$($obj.LocalPort)]"

        if($ProcessName)
        {
          $obj | where{$_.ProcessName -eq $ProcessName}
        }
        elseif($State)
        {
          $obj | where{$_.State -eq $State}
        }
        else
        {
          $obj
        }

          }
    }
 }

  [RegEX]$RegEx =
'\s+(?<Protocol>\S+)\s+(?<LocalAddress>\S+)\s+(?<RemoteAddress>\S+)\s+(?< 0;State>\S+) \s+(?<PID>\S+)'
  $Connections = @{}

  switch -exact ($pscmdlet.ParameterSetName)
  {
    "All"   {
            Write-Verbose " [Get-NetStat] ::
            ParameterSet - ALL"
            $NetStatResults = netstat -ano |
            ?{ $_ -match "(TCP|UDP)\s+\d"}
            Parse-Netstat $NetStatResults
          }
    "Interval"     {
            Write-Verbose" [Get-NetStat] :: ParameterSet -
            Interval"
            for($i = 1 ; $i -le $Interval ; $i++)
            {
            Start-Sleep$Sleep
            $NetStatResults= netstat -ano |
            ?{ $_ -match "(TCP|UDP)\s+\d"}
            Parse-Netstat$NetStatResults | Out-String
          }
        }
    "Loop"        {
        Write-Verbose" [Get-NetStat] :: ParameterSet - Loop"
        Write-Host
        Write-Host
"Protocol LocalAddress LocalPort
        RemoteAddress RemotePort State
        ProcessName
PID"

        Write-Host"-------- ------------ --------- ----------
        ---------- ----- ----------- ---"
        -ForegroundColor
White
               $oldPos = $Host.UI.RawUI.CursorPosition
        [console]::TreatControlCAsInput = $true
        while($true)
        {
          Write-Verbose" [Get-NetStat] :: Getting Netstat
          data"
          $NetStatResults = netstat -ano |
          ?{ $_ -match "(TCP|UDP)\s+\d"}
          Write-Verbose" [Get-NetStat] :: Getting Netstat
                         data from Netstat"
          $Results = Parse-Netstat $NetStatResults
          Write-Verbose" [Get-NetStat] :: Parse-NetStat
          returned $($results.count) results"
          foreach($Result in $Results)
          {
            $Key = $Result.LocalPort
            $Value = $Result.ProcessID
            $msg = "{0,-9}{1,-14}{2,-10}{3,-15}{4,-11}{5,-12}
                    {6,-14}{7,-10}" –f
$Result.Protocol, $Result.LocalAddress, $Result.LocalPort,

$Result.RemoteAddress, $Result.RemotePort, $Result.State,
                        $Result.ProcessName, $Result.ProcessID
            if($Connections.$Key -eq $Value)
            {
              Write-Host$msg
            }
            else
            {
              $Connections.$Key = $Value
              Write-Host$msg -ForegroundColor Yellow
            }
          }
          if($Host.UI.RawUI.KeyAvailable -and (3 -eq
[int]$Host.UI.RawUI.ReadKey("AllowCtrlC,
IncludeKeyUp,NoEcho").Character))
          {
            Write-Host"Exiting now..." -foregroundcolor Yellow
            Write-Host
            [console]::TreatControlCAsInput = $false
            break
          }
          $Host.UI.RawUI.CursorPosition = $oldPos
          start-sleep$Sleep
        }
      }
}
}

For more Information on regular expressions and Windows PowerShell, check out the .NET language reference and Regular-expressions.info from Microsoft.

You can follow SearchWindowsServer.com on Twitter @WindowsTT.

Miss a column? Check out our Scripting School archive.

ABOUT THE AUTHOR
Brandon Shell has been in the IT industry since 1994. He started out as a PC tech and general fix-it guy for numerous companies. In 2007, he joined the PowerShell MVP ranks, and Shell has spent the past several years building his PowerShell knowledge and helping others build theirs.

Dig Deeper on IT operations and infrastructure management

Cloud Computing
Enterprise Desktop
Virtual Desktop
Close