People keep saying that trap{} is on chop block etc since v1 and yet v5 is out and still supports it and not documented that it will be removed etc. Where all this information is coming from? Is there an offical Microsoft position on deprecating using trap{}
I like the way code looks like with all exception being handled in single place and lack of curly braces etc through the code and extra try{}catch{} statements everywhere. I understand there is drawbacks to using it but unless I know for sure it's going to be removed in later versions of Powershell I prefer to use it.
A try catch is a lot more powerful and easy to use than a trap.
The reason why with a tray catch you have ‘statement everywhere’ with ‘extra curly braces’ is because you have the ability to control exactly what you want to catch if an error occurs. The only extra set of curly braces come from the try itself, where you specify what you want to try, not from the catch. With a trap, you can’t easily specify exactly what you want to catch.
Also remember a try…catch is NOT just used to catch exceptions. You could have a try…finally, a try…catch…finally… all things you can’t do with a trap. Finally executes a scriptblock regardless of whether an error occurred or not on what’s inside the try scriptblock. Why is this useful?
Consider the following scenario:
I want to load the Excel COM object and perform some operations on an excel spreadsheet, so there are 4 things I need to do so far:
- Load Excel COM object
- Perform changes to Excel spreadsheet
- Save changes
- Unload COM object
Now I know part of my code may generate errors, which I want to catch, so I place it around a try…catch as follows:
try
{
## Load Excel COM object code...
## Perform changes to Excel spreadsheet...
## Save changes...
## Unload COM object
}
catch
{
## do something with errors
}
The problem with this approach though, is that the moment an error is found, it will immediately jump to the catch script block. So, if I have an error performing changes to the Excel spreadsheet, it will never unload the COM object.
I could do this…
try
{
## Load Excel COM object code...
## Perform changes to Excel spreadsheet...
## Save changes...
## Unload COM object
}
catch
{
## do something with errors
## Unload COM object
}
That way, if an error is not found throughout the execution of the whole try script block it will just unload it when it gets to the end of the try script block… and if an error is found, it will unload it as part of the catch script block.
But… what if the code required to unload my COM object was 100 lines of code? It makes no sense to repeat those 100 lines of code both on the try script block and the catch script block… And this is where the Finally comes in:
try
{
## Load Excel COM object code...
## Perform changes to Excel spreadsheet...
## Save changes...
## Unload COM object
}
catch
{
## do something with errors
}
The problem with this approach though, is that the moment an error is found, it will immediately jump to the catch script block. So, if I have an error performing changes to the Excel spreadsheet, it will never unload the COM object.
I could do this…
try
{
## Load Excel COM object code...
## Perform changes to Excel spreadsheet...
## Save changes...
}
catch
{
## do something with errors
}[
finally
{
## Unload COM object
}
This way, regardless of whether an error is thrown or not, the last thing it does before exiting the whole try…catch…finally statement is execute the code on the finally script block.
So now is the time you say… well, but I could just do without the finally and put my code to unload the COM object after the try…catch altogether, as such:
try
{
## Load Excel COM object code...
## Perform changes to Excel spreadsheet...
## Save changes...
}
catch
{
## do something with errors
}
## Unload COM object
Sure, in this short example it would work. But there are two “problems” with this:
Your try…catch… structure has everything about the COM object. As in… the COM object is created inside the try…catch… the COM object is used inside the try…catch… and when the try…catch finishes, everything that was created inside it should cease to exist, not to pass the job of ‘cleaning up’ to something outside the try…catch. The idea is that the try…catch is a self contained structure.
Also, another massive advantage is this: if you have a try…catch…finally and inside your catch you have a return/break/continue/whatever, then when an error is caught whatever is after the try…catch…finally structure is NOT run, because you told it to exit the function/script/loop/whatever inside the catch. But… the finally statement will ALWAYS be run, even if there is something commanding it to exit on an error.
Here’s a quick example:
function test
{
param
(
[Parameter(ValueFromPipeline = $true)]
$name
)
process{
try
{
"getting service $name"
get-service $name -ErrorAction Stop
}
catch
{
Write-Error "oops, error!"
return
}
finally
{
"I always get executed, even if there's an error AND if there's a return on the catch"
}
"I only get executed when there are no errors (because of the return on the catch). If there was no return on the catch, I'd always get executed too"
}
}
'spooler', 'asd' | test
These are all things that you either can’t do with a trap, or you’ll really struggle. As Don Jones said, try…catch is the intended way. PowerShell is first and foremost a .NET language, and that’s what .NET also uses.