Gerrit Geeraerts
Gerrit Geeraerts

Reputation: 1184

Powershell unexpected behavior

I isolated some code and i have an unexpected behavior.

code:

$objects = @(
    [pscustomobject]@{server="google.com"; some_other_props='some_str'}, 
    [pscustomobject]@{some_other_props='some_str'}, 
    [pscustomobject]@{server='google.com'; some_other_props='some_str'}
)

$objects | % {
    try{
        $result = Test-Connection $_.server
    }catch [System.Management.Automation.ParameterBindingException] {
        Write-Host "no server to ping"
    }
    if($result){
        Write-Host ok
    }
}

expected output:

ok
no server to ping
ok

real ouput:

ok
no server to ping
ok
ok

What am i doing wrong here? from where is the 3rd ok coming??

Upvotes: 0

Views: 187

Answers (3)

js2010
js2010

Reputation: 27576

test-connection doesn't generate a script-terminating or command-terminating exception. Microsoft.com is classically unpingable.

try { test-connection microsoft.com -Count 1 } catch { 'no' }

test-connection : Testing connection to computer 'microsoft.com' failed: Error due to lack of resources
At line:1 char:6
+ try {test-connection microsoft.com -Count 1 } catch { 'no' }
+      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (microsoft.com:String) [Test-Connection], PingException
    + FullyQualifiedErrorId : TestConnectionException,Microsoft.PowerShell.Commands.TestConnectionCommand

You can use the -quiet option to return a boolean and use an if statement instead. Although even without the -quiet, it would work, since the standard output is non-null if the host is up.

if (test-connection microsoft.com -Count 1 -Quiet) {'yes'
} else {'no'}

no

Suprisigly, the '||' operator in Powershell 7 won't work here, because $? is always true after running the command.

test-connection microsoft.com -count 1 -quiet || 'no'

False

Upvotes: 0

Theo
Theo

Reputation: 61208

You need to add parameter -ErrorAction Stop at the end of the Test-Connection cmdlet in order to redirect both terminating and non-terminating errors to the catch block.

Also, your code explicity catches one type of error, so if anything goes wrong, the code will output "no server to ping", even if that is not the case.

You can add another catch block to also display the other exception messages:

$objects = @(
    [pscustomobject]@{server="google.com"; some_other_props='some_str'}, 
    [pscustomobject]@{some_other_props='some_str'}, 
    [pscustomobject]@{server='google.blah'; some_other_props='some_str'}
)

$objects | ForEach-Object {
    $server = $_.server
    try{
        $result = Test-Connection $server -Count 1 -ErrorAction Stop
        if ($result) {
            Write-Host "Ping '$server': OK"
        }
    }
    catch [System.Management.Automation.ParameterBindingException] {
        Write-Host "No server to ping"
    }
    catch {
        Write-Host "Ping '$server': ERROR:  $($_.Exception.Message)"
    }
}

Output:

Ping 'google.com': OK
No server to ping
Ping 'google.blah': ERROR:  Testing connection to computer 'google.blah' failed: Host is unknown

Upvotes: 0

retryW
retryW

Reputation: 693

Your catch doesn't break the current execution, so your if statement will run regardless of if the code throws an exception.

Put your if statement at the bottom inside your catch instead.

$objects | % { 
    try{ 
        $result = Test-Connection $_.server 
        if($result) { 
            Write-Host ok 
        }
    } catch [System.Management.Automation.ParameterBindingException] { 
        Write-Host "no server to ping"
    }  
}

Upvotes: 2

Related Questions