about_Scripting_Try_Catch.help.txt
TOPIC
about_Scripting_Try_Catch SHORT DESCRIPTION A tutorial and suggestions on using Try/Catch. LONG DESCRIPTION I see a lot of scripts that people write so I’d include a short guide on how to properly use Try/Catch. This is the way I think it should be used. Let’s start with a Try/Catch block that might look ok at first glance. Try { Get-Service Foo } Catch { Write-Warning "Oops" Write-Warning $_.Exception.Message } You might 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], ServiceCommandException + FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.Power Shell.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 Try/Catch to work properly, I have to make sure that if there is an exception, that it gets treated as a terminating exception. The best way is to use the common -ErrorAction parameter and set it to Stop. Usually this is done on a per cmdlet basis. 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. The exception was detected and treated as a terminating exception which was caught by the Catch scriptblock. The $_ in the Catch scriptblock is the exception object. 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 auto- matically created a terminating exception. So the fact that I didn't have -ErrorAction Stop was irrelevant. But without a lot of trial and error or experience, I think it is extremely difficult to know in advance what type of error will throw a terminating exception and what won’t. Personally, I think the best approach is to always use -ErrorAction stop on a cmdlet in a Try scriptblock to guarantee you will always get a terminating exception. Finally, depending on your needs you might have a single Try/Catch that handles multiple commands. If so, you can set the ErrorActionPreference at the scope level. Try { $ErrorActionPreference='Stop' $comp = "SERVER01" Write-Host "Getting Process data" -foreground Green $p = Get-Process svchost -computername $comp Write-Host "Getting Service data" -foreground Green $s = Get-Service -computername $comp | where {$_.status -eq 'Running'} Write-Host "Getting OS data" -foreground Green $w = Get-WMIObject win32_operatingsystem -computername $comp } Catch { Write-Warning "There was a problem gathering data" Write-Warning $_.Exception.message } If any of the commands in the Try scriptblock encounter and error, they will be treated as terminating exceptions and code in the Catch scriptblock will run. Also note that if there is a terminating exception in one command subsequent commands will not be run and PowerShell will move on to whatever is after the Catch scriptblock. SEE ALSO about_Try_Catch_Finally |