Sort IPv4 Addresses Correctly

by Mar 13, 2018

In the previous tip we published a super-fast function called Test-OnlineFast, and this function was able to ping an entire IP segment in record time:

 
PS> $iprange = 1..200 | ForEach-Object { "192.168.189.$_" }

PS> Test-OnlineFast -ComputerName $iprange

Address         Online DNSName                            Status           
-------         ------ -------                            ------           
192.168.189.200   True DESKTOP-7AAMJLF.fritz.box          Success          
192.168.189.1     True fritz.box                          Success          
192.168.189.134   True PCSUP03.fritz.box                  Success          
192.168.189.29    True fritz.repeater                     Success          
192.168.189.64    True android-6868316cec604d25.fritz.box Success          
192.168.189.142   True Galaxy-S8.fritz.box                Success          
192.168.189.65    True mbecker-netbook.fritz.box          Success          
192.168.189.30    True android-7f35f4eadd9e425e.fritz.box Success          
192.168.189.10   False                                    Request Timed Out
192.168.189.100  False                                    Request Timed Out
192.168.189.101  False                                    Request Timed Out 
(...)
 

However, the IP address column was not sorted. Of course you could use Sort-Object for that, but since the data in Address is a string, it would be sorted alphanumerically.

Here is a simple trick that you can use to sort IPv4 addresses correctly:

 
PS> Test-OnlineFast -ComputerName $iprange | Sort-Object { $_.Address -as [Version]}
 

Basically, you instruct Sort-Object to convert the data to a Version object which happens to also consist of four numbers, just like an IPv4 address. Since the operator -as yields NULL if the data cannot be converted, any IPv6 address would appear at the top of the list (unsorted).

In case you missed the tip with the actual source code for Test-OnlineFast, here it is again:

function Test-OnlineFast
{
    param
    (
        # make parameter pipeline-aware
        [Parameter(Mandatory,ValueFromPipeline)]
        [string[]]
        $ComputerName,

        $TimeoutMillisec = 1000
    )

    begin
    {
        # use this to collect computer names that were sent via pipeline
        [Collections.ArrayList]$bucket = @()
    
        # hash table with error code to text translation
        $StatusCode_ReturnValue = 
        @{
            0='Success'
            11001='Buffer Too Small'
            11002='Destination Net Unreachable'
            11003='Destination Host Unreachable'
            11004='Destination Protocol Unreachable'
            11005='Destination Port Unreachable'
            11006='No Resources'
            11007='Bad Option'
            11008='Hardware Error'
            11009='Packet Too Big'
            11010='Request Timed Out'
            11011='Bad Request'
            11012='Bad Route'
            11013='TimeToLive Expired Transit'
            11014='TimeToLive Expired Reassembly'
            11015='Parameter Problem'
            11016='Source Quench'
            11017='Option Too Big'
            11018='Bad Destination'
            11032='Negotiating IPSEC'
            11050='General Failure'
        }
    
    
        # hash table with calculated property that translates
        # numeric return value into friendly text

        $statusFriendlyText = @{
            # name of column
            Name = 'Status'
            # code to calculate content of column
            Expression = { 
                # take status code and use it as index into
                # the hash table with friendly names
                # make sure the key is of same data type (int)
                $StatusCode_ReturnValue[([int]$_.StatusCode)]
            }
        }

        # calculated property that returns $true when status -eq 0
        $IsOnline = @{
            Name = 'Online'
            Expression = { $_.StatusCode -eq 0 }
        }

        # do DNS resolution when system responds to ping
        $DNSName = @{
            Name = 'DNSName'
            Expression = { if ($_.StatusCode -eq 0) { 
                    if ($_.Address -like '*.*.*.*') 
                    { [Net.DNS]::GetHostByAddress($_.Address).HostName  } 
                    else  
                    { [Net.DNS]::GetHostByName($_.Address).HostName  } 
                }
            }
        }
    }
    
    process
    {
        # add each computer name to the bucket
        # we either receive a string array via parameter, or 
        # the process block runs multiple times when computer
        # names are piped
        $ComputerName | ForEach-Object {
            $null = $bucket.Add($_)
        }
    }
    
    end
    {
        # convert list of computers into a WMI query string
        $query = $bucket -join "' or Address='"
        
        Get-WmiObject -Class Win32_PingStatus -Filter "(Address='$query') and timeout=$TimeoutMillisec" |
        Select-Object -Property Address, $IsOnline, $DNSName, $statusFriendlyText
    }
    
}

Do you know PowerShell Conference EU 2018, taking place April 17-20 in Hanover, Germany? If you are an advanced PowerShell professional, you shouldn’t miss this year’s agenda: www.psconf.eu: Hover over a session to view its abstract.

With 45 international top speakers including PowerShell inventor Jeffrey Snover, 80 sessions, and workshops, this event is much more than just a conference. It is a one-of-a-kind Advanced PowerShell Training and a very unique opportunity to meet friendly PowerShell gurus, get authoritative answers to even the trickiest PowerShell questions, and bring home fresh ideas.

This conference is not profit-driven, and all speakers volunteer. The delegate fee basically covers venue, food and drinks throughout the conference, evening events with grand dinner, and workshops.

Just don’t wait too long: this unique event is limited to 300 delegates, with 265 seats already taken by the time of this writing. Visit http://www.powershellmagazine.com/2018/02/09/powershell-conference-eu-2018/ for more details, or www.powershell.love for a quick impression of last year.

Twitter This Tip! ReTweet this Tip!