Search code examples
powershellpingunc

Powershell - how to sort UNC paths by ping response time


I'm trying to make a script that would given a list of UNC shares order them by their ping response time. I've managed to come up with something that feels quite like a hack and I'm wondering if anyone has any better idea how to do it purely in "powershell spirit"?

This is my ugly solution:

$shares = Get-Content unc_shares.txt | where {Test-Path $_}
$servers = $shares | ForEach-Object {$_.Substring(2, $_.IndexOf("\", 3) - 2)}

$sortedServers = ($servers |
  ForEach-Object -Process {(Get-WmiObject Win32_PingStatus -filter "Address='$_'")} |
  sort ResponseTime |
  select Address)

foreach($server in $sortedServers)
{
  $shares | where {$_.Contains($server.Address)} | Out-File $sortedListPath -append
}

Solution

  • When all you want to do is sort, just use a ScriptBlock to sort by:

    cat .\uncshares.txt | Sort { Test-Connection -Count 1 -CN $_.split('\')[2] }
    

    Or, you could use an average:

    cat .\uncshares.txt | Sort { Test-Connection -Count 3 -CN $_.split('\')[2] | Measure ResponseTime -Average | Select -Expand Average }
    

    It's true that when you want to add data to an object, you should use Add-Member. In this case, you could add a NoteProperty with the result of the ping, but it's more interesting to add script property (or method) called ping that would actually execute the ping. That is, it will do the ping when you call the ping member:

    $Shares = cat .\uncshares.txt | Add-Member ScriptProperty Ping -Passthru -Value {
                Test-Connection $this.split('\')[2] -Count 1 | 
                Select -Expand ResponseTime }
    
    # Each time you sort by it, it will re-ping, notice the delay:
    $Shares | Sort Ping
    

    You can use the average with this method too:

    $Shares = cat .\uncshares.txt | Add-Member ScriptProperty Ping -Passthru -Value {
                (Test-Connection $this.split('\')[2] -Count 3 | 
                 Measure ResponseTime -Average).Average }
    
    # But this will take even longer:
    $Shares | Sort Ping
    

    As an alternative to Add-Member (when you do NOT want to re-ping every time), you can build up objects using Select-Object, so you could create a Ping object, and then add the Share name back to it like so:

    $unc = cat .\uncshares.txt
    
    ## Gotta love backslashes in regex. Not...
    $unc -replace '\\\\([^\\]+)\\.*','$1' | 
        Test-Connection -Count 1 -CN {$_} | 
        Sort ResponseTime | 
        Select @{n='Server';e={$_.Address}},
               @{n='Share'; e={$unc -match $_.Address}},
               @{n='Ping';  e={$_.ResponseTime}}
    

    That allows you to have drastically different output because you're combining multiple objects together...