3

I'm trying to create a script that will list the servers I need to list along with the latency returned from each server, I need the list to be in ascending or descending order based on ping.

What I've come up with so far has given basically what I want, but if the ping returned is above 100 on one server, the other values are considered to be higher if they start with a value higher than 1.

I understand that I need the ping value returned as an integer, but I haven't been able to figure out exactly how to accomplish that with out removing the server name.

Anyway, here's what I've come up with so far

function TestNetwork {
    $global:array = @("<ServerName>:<ipaddress>"; "<ServerName>:<ipaddress>"; "<ServerName>:<ipaddress>")

    foreach ($str in $global:array) {
        $avg = 0
        $server = $($str -split ":")[0]
        $PingServer = Test-Connection -Count 3 $server
        $avg = ($PingServer | Measure-Object ResponseTime -Average)
        $calc = [System.Math]::Round($avg.Average)
        $output = "{000} | " -f $($calc) + $server
        $Output
    }
}
TestNetwork | Sort numerical -Descending `

and here are the results I get

75 | Server    
73 | Server  
110 | Server  
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
Nick Pope
  • 37
  • 1
  • 1
  • 5
  • 2
    You should output the results with a CustomObject and not format the numbers as strings. – Olaf Apr 25 '18 at 22:27
  • Use a different string format operator to right align the numbers `$output = "{0,3} | " -f $($calc) + $server` –  Apr 25 '18 at 23:24
  • Or let a [RegEx do the padding left on the fly](https://stackoverflow.com/a/5429048/6811411) when sorting `TestNetwork | Sort-Object -desending { [regex]::Replace($_, '\d+', { $args[0].Value.PadLeft(20) }) }` –  Apr 25 '18 at 23:52

3 Answers3

4

There are several ways to address your problem.

You could format the number in a way that aligns integer sort order and string sort order as LotPings suggested:

'{0,3} | {1}' -f $calc, $server

You could also extract the number from the string, convert it back to an integer, and use that as the sorting property:

... | Sort-Object {[int]($_ -replace '(\d+).*', '$1')} -Descending

However, I wouldn't advise either approach. The PoSh way of doing things is to work with objects as Olaf pointed out, so wrap your data in custom objects and convert them to strings only if you have a reason to do so (e.g. to create text output):

foreach ($str in $global:array) {
    ...
    New-Object -Type PSObject -Property ([ordered]@{
        'ComputerName' = $server
        'ResponseTime' = $calc
    })
}

That way you can "naturally" sort your data:

TestNetwork | Sort ResponseTime -Descending | ForEach-Object {
    '{0,3} | {1}' -f $_.ResponseTime, $_.ComputerName
}

or

TestNetwork | Sort ResponseTime -Descending | Export-Csv 'output.csv' -NoType
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • Awesome, thanks for the response, this works perfectly! The reason I wasn't using custom objects is because I needed the output to be a single line that I could grab and convert with a switch. Thanks again, this was a HUGE help! – Nick Pope Apr 26 '18 at 15:19
1

Here is an alternative to the way you are doing things that will give output that can be sorted and formatted more easily:

function Test-Network
{
    $array = ("<ServerName>","<ipaddress>"), ("<ServerName>","<ipaddress>"), ("<ServerName>","<ipaddress>")

    $array | ForEach-Object {                            
        [PsCustomObject]@{
            AvgResponse = [Math]::Round((Test-Connection -Count 3 $_[0] | Measure-Object ResponseTime -Average).Average)
            Server = $_[0]
        }
    }
}

Test-Network | 
    Sort-Object AvgResponse -Descending

Output will look like this:

AvgResponse Server  
----------- ------  
        363 server1
         12 server2
          2 server3

The nice thing about outputting custom objects like this rather than strings is that you can then use further PowerShell techniques on the output, such as sorting, grouping, etc.

Note a couple of other points:

  • Unless you intend to use your array outside the function, there is no need to declare it as 'global'
  • Unless you really need the names in the format "<ServerName>:<ipaddress>", consider storing them differently. I've used an array of arrays, which makes it easier to work with in this case as you avoid having to split a string each time.
  • You should name your functions according to the PowerShell Verb-Noun naming standard, so Test-Network, rather than TestNetwork.
boxdog
  • 7,894
  • 2
  • 18
  • 27
1

Cast string to integer

TestNetwork | Sort {[int]($_.numerical)} -Descending
hamletmun
  • 51
  • 3