Event 5: My way…

It looks like I missed another deadline. Luckily, I’m not participating this year, so the only negative outcome of it is the fact that I will post my solution after most of you already seen entries done by others. I haven’t (I promise!) so you can expect many mistakes you’ve avoided by giving it second, third or fourth thought. I also had no chance to test it on some “real” material like some participants did (yes, I’ve seen your tweets and I’m sure you did it better, thank you for sharing! Puszczam oczko ). Wonder how I could improve it… Surely – will find out soon. One note: I decided to focus on IPv4. Mainly because I started so late, and didn’t wanted to spent too much time on handling IPv6 addresses, that are less obvious to match with regular expression (IMO).

Beginner

To be honest there is no huge difference between both categories, at least from what I understand. The only difference is that I don’t have to handle patterns/ path provided by Dr. Scripto. As usually – I did it in one line of code:

Get-ChildItem -Path .\LogFiles -Recurse -Filter *.LOG |            
    Select-String -Pattern '\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b' -AllMatches |            
    select -ExpandProperty Matches |             
    ForEach-Object -Begin {            
        $IPs = @{}            
    } -Process {            
        $IPs.($_.Value) = ''            
    } -End {            
        $IPs.Keys            
    }            

Pattern is “stolen” from one of the first results I’ve found with google. I don’t use Get-Content – in my experience using Select-String directly is a lot quicker. To get unique IPs I use hashtable keys. I could improve it slightly by checking if given key is already present, but here – I wanted to be as concise as possible.

Advanced

For this category I decided to try make my solution more modular. That’s why I have two functions defined: one converts human-readable IP pattern (e.g. 10.*) to something that regular expression could work with. I decided that I want to support two types of patterns: beginning (with optional *) or with 4 tokens and any token in IP address covered by wildcard, so both 10.*, 10 and *.*.*.25 would work. To convert one pattern to other I defined function:

function ConvertTo-RegexPattern {            
[CmdletBinding()]            
param (            
    $Pattern = '*'            
)            
    $SingleToken = '(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'            
    $Tokens = @(            
        $Pattern.Split('.') -replace '\*', $SingleToken            
    )            
                
    Write-Verbose "Pattern contains $($Tokens.Count) tokens."            
    while ($Tokens.Count -lt 4) {            
        $Tokens += $SingleToken            
    }            
    "\b(?:$($Tokens -join '\.'))\b"            
}            

Logic that is taking care of matching actual IPs is almost the same as for beginner category, only parameterized:

function Get-MatchingIP {            
param (            
    [ValidateScript({            
        if (Test-Path -PathType Container -Path $_) {            
            $true             
        } else {            
            throw "Specify path to folder, $_ could not be found"            
        }            
    })]            
    [string]$Path,            
    [string]$Filter = '*.LOG',            
    [string]$Pattern = $Script:RegexPattern            
)            
    Get-ChildItem -Path $Path -Recurse -Filter $Filter |            
        Select-String -Pattern $Pattern -AllMatches |            
        select -ExpandProperty Matches |             
        ForEach-Object -Begin {            
            $IPs = @{}            
        } -Process {            
            if (-not $IPs.ContainsKey($_.Value)) {            
                $IPs.($_.Value) = ''            
            }            
        } -End {            
            $IPs.Keys            
        }            
            
}            

Both functions are defined in the begin block (that is moved to the end Puszczam oczko) and consumed in the end block (that is first in the script):

end {            
    $RegexPattern = ConvertTo-RegexPattern -Pattern $Script:Pattern            
    Write-Verbose $RegexPattern            
    Get-MatchingIP -Path $Script:Path            
}            

Remaining pieces include parameters for whole script, together with validation for input IP pattern, just as precaution:

param (            
    [ValidateScript({               
        $_.Split('.') | Foreach-Object -Process {            
            if ($_ -notmatch '^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|\*)$') {            
                throw "Specify valid IP pattern, e.g. 10.*"            
            }            
        } -End {            
            $true            
        }            
    })]            
    [string]$Pattern = '*',            
    $Path = '.\LogFiles'            
)            

So it will check if all tokens used are either valid IP token (0-255) or ‘*’. And with that done I can finally take a look what others came up with… Uśmiech

Advertisement

1 thought on “Event 5: My way…

  1. Pingback: Last events: my notes and scripts. | PowerShell.org

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s