Handling Errors


PowerShell divides errors into two categories:

  1. Terminating. Causes your script or command to stop executing.

  2. Non-terminating. Even though a problem still exists, the script or command is allowed to continue running.

When an error occurs, the error itself is represented by an object called ErrorRecord. This object contains an exception, which is essentially a fancy word for error. An exception also includes other information about why and where the error occurred. Like any other object, ErrorRecord has properties that you can examine:

  • Exception. This is the error that occurred. It's an object in and of itself. For example, Exception.Message contains an English description of the error.

  • TargetObject. This is the object that was being operated when the error occurred. This may be Null if there was no particular object involved.

  • CategoryInfo. This divides all errors into a few dozen broad categories.

  • FullyQualifiedErrorId. This property identifies the error more specifically. In fact, it is the most specific identifier.

  • ErrorDetails. May be Null, but when present contains additional information. It's actually a subobject called ErrorDetails.Message. One of its properties is the most specific possible English description of the error.

  • InvocationInfo. Tells you the context in which the error occurred such as a cmdlet name or script line. May be Null.

A special variable, $error, is used to store the most recent ErrorRecord objects. In fact, by default the most recent 256 errors are stored. The $error variable is an array. For example, $error[0] contains the most recent error and $error[1] is the one before that. Each element of $error is an ErrorRecord object. For example, to see the error text for the most recent error, you would examine:

 $error[0].Exception.Message 

When an error occurs, you can examine $error to determine if your script can do anything about the error. However, before your script has the opportunity to do so, you will have to trap the error.

Error Actions

Most cmdlets support the ubiquitous ErrorAction argument, which is aliased as just "ea." This argument specifies what should happen if the cmdlet encounters a non-terminating error. The default behavior is Continue, which means the cmdlet displays the error and tries and continue executing the cmdlet or script. Other options include:

  • Stop. Makes the cmdlet stop executing.

  • Inquire. Asks the user what to do.

  • SilentlyContinue. Continues without displaying any clues as to what went wrong.

Here's an example of SilentlyContinue:

 $a = get-wmiobject Win32_OperatingSystem -ea stop 

This executes Get-Wmiobject. If something goes wrong, it will stop rather than continuing. However, if you've defined a trap handler, which we'll discuss next, then the trap handler will still execute after the cmdlet stops. Trap handlers always execute no matter what ErrorAction you specify.

Trapping Errors

Trapping in PowerShell can be fairly complicated. When an error occurs in a script, an exception is "thrown" or "raised." That exception is delivered to the exception handler that is called the trap handler. Following the execution of the handler, the session state established by ErrorPolicy settings is checked to determine whether or not the script should continue running. If a specific trap handler has not been defined, the exception will simply be delivered to the output mechanism. This usually means the exception will be displayed and the script will halt. If a trap handler is defined, it may reset the ErrorPolicy, which determines whether or not the script will continue after the error is resolved.

Let's briefly summarize how this works. If a trap handler has been defined, the handler is executed when an error occurs. The handler has access to $error to see what went wrong. It can also set the ErrorPolicy to determine whether or not the script continues. Defining the trap handler looks a bit like this:

 Trap [ExceptionType] {   # statements go here   # $_ represents the ErrorRecord that was thrown   Return [argument] | break | continue } 

The [ExceptionType] is the type of exception you want to trap. This allows you to define a different trap handler for different types of errors or exceptions. However, you do not have to include [ExceptionType]. If you don't, the trap handler will handle any exceptions that occur. You can actually define multiple traps for the same exception. If that exception occurs, all of the traps will execute in the order in which they're defined.

Within the trap handler, the special $_ variable represents the ErrorRecord that caused the trap handler to be executed in the first place.

At the end of the trap handler, you have three options:

  • Continue. This causes script execution to continue at the line of code following the line that caused the error.

  • Break. This causes the current scope to stop executing.

  • Return [argument]. This exits the current scope, optionally returning whatever argument you specify.

If multiple trap handlers are executed, then the Continue/Break/Return of the last-executed handler is the one that takes effect.

Understanding how these three options work requires you to understand a bit about trap scope. If you don't specify any of these three options, then the trap handler will exit returning $_, which is the error that caused the trap handler to be called in the first place.

Trap Scope

Remember from Chapter 1 that PowerShell supports scopes. Essentially, the shell is the global scope. Running a script begins a new scope in which the script itself runs. In addition, each function that's executed is a unique scope. You can define a unique trap handler within each of these scopes. Scripts can have trap handlers, while functions can have self-contained trap handlers that are private to the function. When an exception is thrown, a trap handler in the current scope is executed if one is available.

When you exit a trap handler using Continue, the next line of code in the same scope is executed. The Break keyword exits the current scope and goes up one level to the calling scope, passing the exception up to the calling scope. If another trap handler is defined, it can be called at this point.

The Return keyword does more or less the same thing, except that the specified argument is passed to the calling scope and no exception is thrown. So, if the trap handler lives inside a function, the Return keyword will append to the function's return value, and the function exits normally as if nothing bad occurred.

Throwing Your Own Exceptions

You can use the Throw keyword to throw an exception. This is basically the same thing that happens when a line of script causes an error, except you're sort of causing the error on purpose. This can be used to pass the error up to a parent scope for handling. For example:

Script1.ps1

image from book
 trap {  write-host "YIKES!!!"  throw $_ } script2 
image from book

Script2.ps1

image from book
 trap {   write-host "In Script2"   break } $a = get-content C:\nofile.txt -erroraction stop 
image from book

Here, Script1 calls Script2. Script2 defines a trap handler and then attempts to get the content of a nonexistent file. Note that the Get-Content cmdlet is run with the -erroraction argument, specifying that should an error occur, the cmdlet should stop. The default action is to Continue. Other choices would be SilentlyContinue or Inquire (e.g., ask the user what to do). So, when an error occurs, since C:\nofile.txt doesn't exist an exception is thrown by Get-Content. It stops running, and the exception is picked up by the trap handler in Script2. This trap handler outputs "In Script2" and then breaks. This causes execution of Script2 to stop, and execution to return to the calling scope, which is Script1 or the scope that called Script2 in the first place.

The Break keyword passes the exception up to Script1, which has its own trap handler defined. It outputs "YIKES!!!" and then throws an exception. The exception it throws is $_, which is the exception that caused the trap handler to run. Throwing an exception is kind of like using Break - the current scope exists, and the specified exception is passed to the calling scope. Assuming Script1 was launched interactively from within PowerShell, then the global scope is the calling scope and will receive the exception in $_.

The output, looks like this:

 PS> script1 In Script2 YIKES!!! The path 'nofile.txt' does not exist. At c:\ps\scripts\script2.ps1:6 char:17 + $a = get-content <<<< C:\nofile.txt -erroraction stop 

You can see where Script1 was executed from a PowerShell prompt. Script2 was called, and its trap handler output "In Script2" before passing the exception back to Script1. Script1's trap handler outputs "YIKES!!!" before passing the exception back up the line to the global scope. PowerShell's behavior is to display the error, "The path 'nofile.txt' does not exist" and then list the original location where the error occurred.

You can also use Throw to throw a text error:

 Throw "this is my error" 

PowerShell constructs an actual ErrorRecord object out of this. The exception is a generic RuntimeException, the ErrorID is the string you provided (which will also go into the exception's Message property), the ErrorCategory is OperationStopped, and the targetObject is the string you provided.

If you just call Throw with no argument, the ErrorRecord's exception is RuntimeException. The ErrorID is "ScriptHalted," which also goes into the exception's Message property. The ErrorCategory is OperationStopped, and the targetObject property is Null.

Tips for Error Trapping

Take a look at TrapTest.ps1:

TrapTest.ps1

image from book
 function CheckWMI ($computer) {   trap {     write-host "An error occured: "     write-host "ID: " $_.ErrorID     write-host "Message: "$_.Exception.Message     throw "Couldn't check $computer" } $a = get-wmiobject Win32_OperatingSystem `  -property ServicePackMajorVersion `  -computer $computer -ea stop write-host "$computer : " $a.ServicePackMajorVersion } write-host "Service Pack Versions:" CheckWMI("DON-PC") CheckWMI("TESTBED") 
image from book

Assuming DON-PC exists and TESTBED doesn't, this produces the following output:

 PS C:\> test\traptest Service Pack Versions: DON-PC : 2 An error occured: ID: Message:  Command execution stopped because the shell variable "ErrorActionPreference" is set to Stop: The RPC server is unavailable. (Exception from HRESULT: 0x800706BA) Couldn't check TESTBED At C:\test\traptest.ps1:8 char:10 +     throw  <<<< "Couldn't check $computer" 

Notice that -ea stop was specified for the cmdlet, ensuring that it would stop execution and allow the trap to execute. Let's make one small change to the original script: Remove the -ea argument, allowing the default of Continue to take place.

Here's the revised output:

 PS C:\> test\traptest Service Pack Versions: DON-PC :  2 Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA) At C:\test\traptest.ps1:11 char:21 +   $a = get-wmiobject  <<<< Win32_OperatingSystem ` TESTBED : PS C:\> 

See the difference? The trap didn't get to execute in this case-the error occurred right at the cmdlet. So, whenever possible, make sure you're executing cmdlets with an appropriate ErrorAction argument, and defining a trap to handle whatever errors might crop up.



Windows PowerShell. TFM
Internet Forensics
ISBN: 982131445
EAN: 2147483647
Year: 2004
Pages: 289

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net