Summary: Ed Wilson, Microsoft Scripting Guy, talks about error handling and two types of errors.
Hey, Scripting Guy! I really don’t get it. Everyone tells me I need to use Try/Catch/Finally to catch errors, but it simply won’t work. I am not sure if there is a bug in Windows PowerShell, but I wish you guys would fix it because it doesn't work. Can you help me?
—SH
Hello SH,
Microsoft Scripting Guy, Ed Wilson, is here. It is becoming fall around here in central Florida. In fact, last night it got down to about 69 degrees Fahrenheit. I mean, it was cold. It is something to look forward to because it has been hot and muggy.
SH, structured error handling in Windows PowerShell does in fact work. The reason that sometimes it may appear to not work is because of the difference in the types of errors. Essentially, there are two types of errors: terminating and non-terminating. A non-terminating error is one that Windows PowerShell can recover from, and it can continue doing things. A terminating error is one that means "it is over, and Windows PowerShell can’t do anything else."
For example, if I attempt to perform a directory listing for a directory that does not exist, Windows PowerShell will simply see that the directory does not exist, throw an error, and continue to the next folder. Most of the times, this is exactly what one wants.
On the other hand, if I attempt to divide by zero, that is a terminating error, and there is nothing else to do.
Try/Catch/Finally will handle terminating errors. When a terminating error arises (in the Try section), it will catch the error (in the Catch section). What I do in the Catch section is all the error handling stuff. For example, I may want to display the current values of variables and see why my script is attempting to divide by zero, I may want to initialize variables and reset counters (to prevent dividing by zero), or I may simply want to display a message that the error was caught and go on to the next section of the code.
Regardless of what I do in the Try or the Catch section, the Finally section will run. This is great because it is able to, for example, email me the results of my script run, close connections, reboot the computer, or whatever I need to do, or whatever I want to do every time the script runs.
What do I try?
So what do I try? Well, in the Try section of my script, I have the code that I know, or that I think may create an error if conditions are not right. Therefore, I want to add the error handling around the Try section.
If things go wrong, what do I want to happen? That goes into the Catch section.
In this script, I try to divide by zero. The code that I want to run goes into the script block. Here is the Try:
Try {1/0}
What do I catch?
I can get really precise with the types of errors that I catch. For example, if I attempt to write to a non-existent file, I can catch the File not found error message, create the file, and then in the Finally section, write to the file.
But it is also possible to be really generic. For example, all errors derive from System.Exception class. So, if I want to say, “Dude, I don’t care what happens, I want to catch the error, and then I want to do something," then I catch [System.Exception].
Of course, what I do when I catch an instance of [System.Exception] goes into the script block. This is shown here:
Catch [System.Exception] {"Caught the exception"}
What do I do?
After I have tried something, and I have caught any error that appears, now what? Well, I don’t’ have to do anything, if I don’t need to or if I don’t want to. But if I do want to do something, such as reset the system, re-initialize variables, or roll back or forward transactions, I can do it in the Finally block.
The Finally block means that whether things succeeded or failed, I will do it. So, maybe all I do is write to an event log, or send an email confirmation. Here is an example of the Finally block:
Finally {$error.Clear() ; "errors cleared"}
The complete Try/Catch/Finally
If I try to divide by zero, it would generate an error message, such as the one shown here:
PS C:\Users\mredw> 1/0
Attempted to divide by zero.
At line:1 char:1
+ 1/0
+ ~~~
+ CategoryInfo : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorId : RuntimeException
But as it turns out, I put it in the Try block. Now, if any error occurs, I want to display a message that says an error was caught. Finally, I want to reset my error count. Here is the complete Try/Catch/Finally block:
Try {1/0}
Catch [System.Exception] {"Caught the exception"}
Finally {$error.Clear() ; "errors cleared"}
When I run it, I see two things: the exception was caught and the errors were cleared:
SH, that is all there is to using Try/Catch/Finally. Error Handling Week will continue tomorrow when I will talk about more cool stuff.
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