Weekend Scripter: Using Try, Catch, Finally Blocks for PowerShell Error Handling

Weekend Scripter: Using Try, Catch, Finally Blocks for PowerShell Error Handling

  • Comments 2
  • Likes

Summary: Microsoft PFE, Ashley McGlone, talks about using Try, Catch, Finally blocks for error handling in Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. Today’s guest blogger is Ashley McGlone, a Microsoft premier field engineer. Ashley is a popular speaker at our Windows PowerShell Saturday events. He regularly blogs about Active Directory and Windows PowerShell at Goatee PFE. You can follow him on Twitter as @GoateePFE. Take it away, Ashley...

Why do scripts have errors? That is a long philosophical debate, and there is more than one correct answer. The generally accepted answer is that they are written by humans, and humans are not perfect. Even when considering all of the reasons there are errors in scripts, the root cause is usually a difference in expectations. I expect the code or data to be X, but really it is Y. And that is why we need error handling.

There are many ways to handle errors in Windows PowerShell, including:

  • $Error.Clear(); Do-Something; If ($Error) {..} Else {..}
  • Trap
  • $ErrorActionPreference

Try, Catch, Finally is similar to a Trap block. Trap blocks generally catch any errors in the scope of the entire script or function. The beauty of Try, Catch, Finally is that it is like a localized Trap for a specific block of commands. This gives you great flexibility in your error handling. It generally works like this:

Try {

 # Do something tricky

}

Catch {

 # Run this if a terminating error occurred in the Try block

 # The variable $_ represents the error that occurred

 $_

}

Finally {

 # Always run this at the end

}

Tip  Introduced in Windows PowerShell 3.0, you can use CTRL-J in the ISE to insert a Try, Catch, Finally snippet template to save you some typing.

We can also catch multiple errors. Here is an example from the ISE snippet:

try

{

 1/0

}

catch [DivideByZeroException]

{

 Write-Host "Divide by zero exception"

}

catch [System.Net.WebException],[System.Exception]

{

 Write-Host "Other exception"

}

finally

{

 Write-Host "cleaning up ..."

}

Finding .NET exceptions to catch

Following the Catch keyword, you can add .NET exception types as shown in the previous script example. (Fancy programmers call errors “exceptions.”) These are optional. So where can I find that fanciness to put after the Catch? You can sometimes find these on MSDN. For me, the fastest way is using this little trick:

$Error[0] | fl * -Force 

Look at the following example output when we try to divide by zero. Then notice the $Error output from the second command. Usually, the .NET exception follows the ‘-->’ in the output:

PS C:\> 1/0

Attempted to divide by zero.

At line:1 char:1

+ 1/0

+ ~~~

 + CategoryInfo   : NotSpecified: (:) [], RuntimeException

 + FullyQualifiedErrorId : RuntimeException

 

PS C:\> $Error[0] | fl * -Force

PSMessageDetails  :

Exception    : System.Management.Automation.RuntimeException: Attempted to divide by zero. -->

      System.DivideByZeroException: Attempted to divide by zero.

       --- End of inner exception stack trace ---

       at System.Management.Automation.IntOps.Divide(Int32 lhs, Int32 rhs)

       at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)

       at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)

       at   System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

TargetObject   :

CategoryInfo   : NotSpecified: (:) [], RuntimeException

FullyQualifiedErrorId : RuntimeException

ErrorDetails   :

InvocationInfo  : System.Management.Automation.InvocationInfo

ScriptStackTrace  : at <ScriptBlock>, <No file>: line 1

PipelineIterationInfo : {}

That’s how we find what to catch (or trap) when handling errors. You have to produce the error once, get the exception string from the extended $Error details, and then put that into square brackets following Catch. In this case either of the following would work:

catch [DivideByZeroException]

catch [System.DivideByZeroException]

This works for most error handling situations.

Tap...tap...tap...Is this thing on?

Sometimes Try, Catch, Finally will not catch your error. That’s because there are two kinds of errors in Windows PowerShell: terminating and non-terminating. For example, when I type:

PS C:\> dir HKLM:

I get errors in the middle of the output, but it keeps going. That is called a non-terminating error. However, if I try to divide by zero as in the previous example, that is a terminating error that stops the entire script.

You can force errors to terminate and hit your Catch block by using either of these methods:

  • $ErrorActionPreference = ‘Stop’
  • Use the common parameter: -ErrorAction Stop

In the interest of time, I will refer you to this previous blog post for a good explanation about how this works: 2014 Winter PowerShell Scripting Games Wrap Up #2.

How I’ve used Try, Catch, Finally

Most of my scripting revolves around the Active Directory module. Recently I was trying to use Get-ADObject with the ErrorAction parameter. Unfortunately, the cmdlet did not seem to obey the ErrorAction common parameter very well.

# No joy

$a = Get-ADObject -Identity $Id -Properties $Prop -ErrorAction SilentlyContinue

I need this functionality, because sometimes the object I was querying may not exist. This syntax would break the script if an error occurred, ignoring the ErrorAction parameter. To work around this, I enclosed the offending line of script in a Try block, and then I handled the error in the Catch block. Here is what I used:

try {

 $a = Get-ADObject -Identity $Id -Properties $Prop

}

catch {

 $a = $null

}

Notice that I left off the optional Finally block. This code met my needs perfectly.

I hope you’ve enjoyed learning about Try, Catch, Finally today. Don’t forget to read the Help for more information:

PS C:\> Get-Help about_Try_Catch_Finally

See these Windows PowerShell Help topics for related information:

  • about_Preference_Variables
  • about_CommonParameters
  • about_Throw
  • about_Trap

~Ashley

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi Ed,
    I am getting an error that I can not get my try / catch block to work on. I wrote a script that moves files that are no longer accessible to a Recycle Bin I created. I am now trying to count them. I am getting an error with this line:
    $pGSizeOb = Get-ChildItem $modInstIDLocation -Recurse | Measure-Object -Property length -sum
    When there are only empty folders in the folder $modInstIDLocation I get an error because the length property does not exist.
    $Error[0] | fl * -Force
    gives me feed back (in red instead of white like I get with the divide by 0 example. I guess that means I am really bad for trying to get length on a folder :) )
    the issue is I can't figure out from the feedback what the error code is so my catch / try does not seem to be working.
    It is not the end of the world. the script still works. I just hate getting all those red errors.
    Thanks for any help you can give me.
    Peter

  • Get-ChildItem returns System.IO.DirectoryInfo objects and System.IO.FileInfo objects. Count the FileInfo.Length to get the foldersize.
    $size = 0; ForEach($file in (Get-ChildItem -Path .\ -Recurse | Where-Object -FilterScript {$_.GetType() -match 'FileInfo'})){$size += $file.Length}; Write-Output $size