In looking at entries in this year's Scripting Games, as well as posts I see in PowerShell forums, I thought I'd post a short guide to properly using Try/Catch. This is the way I think it should be used. Let's start with a Try/Catch block that might look ok.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
Try {
Get-Service Foo
}
Catch {
Write-Warning "Oops"
Write-Warning $_.Exception.Message
}
You would expect that if the service FOO can't be found that the code in the Catch script block will run.
PS C:\work> .\trydemo.ps1
Get-Service : Cannot find any service with service name 'Foo'.
At C:\work\trydemo.ps1:4 char:14
+ Get-Service <<<< Foo
+ CategoryInfo : ObjectNotFound: (Foo:String) [Get-Service], Serv
iceCommandException
+ FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.
Commands.GetServiceCommand
Nope. I got an exception, but my code in the Catch script block did not run. You can only catch terminating exceptions. In order for my Try/Catch to work properly, I have to make sure that if there is an exception it gets treated as a terminating exception. The best way is to use the common -ErrorAction parameter and set it to Stop.
Try {
Get-Service Foo -ErrorAction Stop
}
Catch {
Write-Warning "Oops"
Write-Warning $_.Exception.Message
}
The parameter has an alias of -ea which you'll often see used. Now let's see what happens:
PS C:\work> .\trydemo.ps1
WARNING: Oops
WARNING: Cannot find any service with service name 'Foo'.
PS C:\work>
That's what I was expecting. Now for the tricky part. Look at this variation.
Try {
Get-Service -ComputerName Bogus
}
Catch {
Write-Warning "Oops"
Write-Warning $_.Exception.Message
}
What happens when I run this?
PS C:\work> .\trydemo.ps1
WARNING: Oops
WARNING: Cannot open Service Control Manager on computer 'Bogus'. This
operation might require other privileges.
How about that? Even without -erroraction, my catch code still ran. Based on this I can safely deduce that the remote connection failure created a terminating exception. But without a lot of trial and error I think it is impossible to know in advance what type of error will throw a terminating exception and what won't. Personally, I think the best thing is to always use -ErrorAction stop on a cmdlet in a Try scriptblock to guarantee you will always get a terminating exception.
Jeff –
For all of your users when they get “tired typing syndrome”. -ea 0
Good blog – I will keep a link to post for others.
One downside is that it isn’t clear in a script what 0 means. But in the console makes sense.
Isn’t -ea 0 the shorthand for ‘SilentlyContinue’?
That seems to be the case. Hardly intuitive which is why I would avoid it in a script.
PS C:\work> [enum]::GetValues([system.management.automation.actionpreference]) |
foreach {"$_ = $([int]$_)"}
SilentlyContinue = 0
Stop = 1
Continue = 2
Inquire = 3
hi,
you can also use ‘ErrorActionPreference’ and finally block:
Try {
$ErrorActionPreference = 'Stop'
Get-Service Foo
Get-Process Boo
}
Catch {
Write-Warning "Oops"
Write-Warning $_.Exception.Message
}
finally{
$ErrorActionPreference = 'SilentlyContinue'
}
‘sorry’
Try {
$ErrorActionPreference = ‘Stop’
Get-Service Foo
Get-Process Boo
}
Catch {
Write-Warning “Oops”
Write-Warning $_.Exception.Message
}
finally{
$ErrorActionPreference = ‘Continue’
}
That is certainly an easier way if you are including a number of commands in a single Try scriptblock.