$? and $LASTEXITCODE

This post is part of the Second Wednesday Demo Session, Click here for more info about additional demo posts.

In this demo I showed some examples on Error handling in Windows PowerShell. First we took a look at the $? and $LASTEXITCODE variables.
The $? variable displays a Boolean value that represents the success or failure of the last command. as an example, we’ll first run a command that works
and examine the $? variable.


PS > cd 'C:\Program Files'
PS > $?

True

In the example we use cd (alias for Set-Location) to change working location to a specific location. Notice that $? returned True.

If we try to set the location to a non-existing folder an error occurs and $? returns False.


PS > cd C:\FolderThatDoesntExist

Set-Location : Cannot find path 'C:\FolderThatDoesntExist' because it does not exist.
At line:1 char:3
+ cd <<<<  C:\FolderThatDoesntExist
    + CategoryInfo          : ObjectNotFound: (C:\FolderThatDoesntExist:String) [Set-Location], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.SetLocationCommand

PS > $?

False

Next we took a look at $LASTEXITCODE. The variable returns a number that represents the exitcode of the last script or application.
In this example we used ping.exe.


PS > ping 127.0.0.1 -n 1

Pinging 127.0.0.1 with 32 bytes of data:
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128

Ping statistics for 127.0.0.1:
    Packets: Sent = 1, Received = 1, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms

PS > $LASTEXITCODE

0

When we tried to ping a nonexisting machine the $LASTEXITCODE variable returned 1 instead.


PS > ping NonExistingMachine -n 1

Ping request could not find host NonExistingMachine. Please check the name and try again.

PS > $LASTEXITCODE

1

Rating 3.00 out of 5
[?]

$Error

This post is part of the Second Wednesday Demo Session, Click here for more info about additional demo posts.

The $error variable contains an array of errors generated in the current session. If we want to check the latest occured error we can type:


PS > cd C:\FolderThatDoesntExist

Set-Location : Cannot find path 'C:\FolderThatDoesntExist' because it does not exist.
At line:1 char:3
+ cd <<<<  C:\FolderThatDoesntExist
    + CategoryInfo          : ObjectNotFound: (C:\FolderThatDoesntExist:String) [Set-Location], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.SetLocationCommand

PS > $Error[0]

Set-Location : Cannot find path 'C:\FolderThatDoesntExist' because it does not exist.
At line:1 char:3
+ cd <<<<  C:\FolderThatDoesntExist
    + CategoryInfo          : ObjectNotFound: (C:\FolderThatDoesntExist:String) [Set-
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.SetLocationC

We can also list detailed information about the error by pipe:ing the object to Format-List as shown below.


PS > $Error[0] | Format-List -Force


Exception             : System.Management.Automation.ItemNotFoundException: Cannot fi
                         because it does not exist.
                           at System.Management.Automation.LocationGlobber.ExpandMshG
                        NonexistingPaths, PSDriveInfo drive, ContainerCmdletProvider
                        ntext)
                           at System.Management.Automation.LocationGlobber.ResolveDri
                        tProviderContext context, Boolean allowNonexistingPaths, Cmdl
                           at System.Management.Automation.LocationGlobber.GetGlobbed
                        h, Boolean allowNonexistingPaths, CmdletProviderContext conte
                        ce)
                           at System.Management.Automation.SessionStateInternal.SetLo
                        Context context)
                           at System.Management.Automation.PathIntrinsics.SetLocation
                        t context)
                           at Microsoft.PowerShell.Commands.SetLocationCommand.Proces
TargetObject          : C:\FolderThatDoesntExist
CategoryInfo          : ObjectNotFound: (C:\FolderThatDoesntExist:String) [Set-Locati
FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.SetLocationCommand
ErrorDetails          :
InvocationInfo        : System.Management.Automation.InvocationInfo
PipelineIterationInfo : {0, 1}
PSMessageDetails      :

If we want to count the number of errors in the $error variable we use the Count property.


PS > $error.Count

1

And we can even clear the error list by using the Clear() method.


PS > $error.Clear()

The number of errors that are listed in the $error variable is determined in $MaximumErrorCount. By default the variable is set to 256, meaning that the error
list holds a maximunm of 256 errors. We can, of course change this to a higher number by typing:


PS > $MaximumErrorCount = 500

Rating 3.00 out of 5
[?]

Trap

This post is part of the Second Wednesday Demo Session, Click here for more info about additional demo posts.

In order to catch and handle errors in PowerShell we can use the Trap keyword. Trap has the following Syntax:


trap [[error type]] {statement list}

If we try to merge a value of the type System.Int32 with a value of type System.String an error occurs.


PS > [int]1 + [string]"Hello"

Cannot convert value "Hello" to type "System.Int32". Error: "Input string was not in
a correct format."
At line:1 char:9
+ [int]1 + <<<< [string]"Hello"
+ CategoryInfo : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorId : RuntimeException

We can get the type of error that occured by checking the InnerException.


PS > $Error[0].Exception.InnerException.GetType().FullName

System.Management.Automation.PSInvalidCastException

With this information, we can handle the errors of the type System.Management.Automation.PSInvalidCastException using a simple trap. In the example below
we create a funtion that shows how to handle these types of errors.


function TrapError([scriptblock]$Command) {
  Trap [System.Management.Automation.PSInvalidCastException] {
    Write-Host "My Custom Error Message" -ForegroundColor Red
    Continue
  }
  & $Command
}

When we call the function our custom error message is returned instead of the default error message.


PS > TrapError -Command { [int]1 + [string]"Hello" }

My Custom Error Message

But what if we generate another kind of error. Let's try to divide 1 by $null.


PS > TrapError -Command { 1/$null }

Attempted to divide by zero.
At line:1 char:24
+ TrapError -Command { 1/ <<<< $null }
    + CategoryInfo          : NotSpecified: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : RuntimeException

This time the error was not handled since it's a different type of error. We can of course modify our function to handle these types of errors as well.


function TrapError([scriptblock]$Command) {
  Trap [System.Management.Automation.PSInvalidCastException] {
    Write-Host "My Custom Error Message" -ForegroundColor Red
    Continue
  }

  Trap [System.DivideByZeroException] {
    Write-Host "1/`$null doesn't really work.." -ForegroundColor Red
    Continue
  }

  & $Command
}

If we try the same command again, the error is handled.


PS > TrapError -Command { 1/$null }

1/$null doesn't really work..

We can also add a Trap keyword without a specific error type to handle other types of errors that may occur.


function TrapError([scriptblock]$Command) {
  Trap [System.Management.Automation.PSInvalidCastException] {
    Write-Host "My Custom Error Message" -ForegroundColor Red
    Continue
  }

  Trap [System.DivideByZeroException] {
    Write-Host "1/`$null doesn't really work.." -ForegroundColor Red
    Continue
  }

  Trap {
    Write-Host "This really doesn't work.." -ForegroundColor Red
    Continue
  }

  & $Command
}

If we try to create an instance of a .NET Object and specify a nonexisting class, the error is handled by the trap.


PS > TrapError -Command { New-Object System.Blah }

This really doesn't work..

Some Errors are handled in other ways in commands and scripts. a typical example is the Get-ChildItem cmdlet.
If we use our function and use Get-ChildItem and try to retrieve a folder that doesn't exist, the cmdlet will handle
the error.


PS > TrapError -Command { Get-ChildItem C:\Blah }

Get-ChildItem : Cannot find path 'C:\Blah' because it does not exist.
At line:1 char:35
+ TrapError -Command { Get-ChildItem <<<<  C:\Blah }
    + CategoryInfo          : ObjectNotFound: (C:\Blah:String) [Get-ChildItem], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

We can override this and manage the error in our trap by setting the ErrorAction parameter to Stop.


PS > TrapError -Command { Get-ChildItem C:\Blah -ErrorAction Stop }

This really doesn't work..

Rating 4.00 out of 5
[?]

Try/Catch/Finally

This post is part of the Second Wednesday Demo Session, Click here for more info about additional demo posts.

In PowerShell V2 we can use Try/Catch and Finally to control how scripts handle errors. Just as with the Trap statement, we can handle different types of errors.
Here’s a short function that shows how Try/Catch/Finally works.


function TryCatchError([scriptblock]$Command) {
  Try {
    & $Command
  }
  Catch [System.Management.Automation.PSInvalidCastException] {
    Write-Host "A PSInvalidCastException error occured" -ForegroundColor Red
  }
  Catch [System.DivideByZeroException] {
    Write-Host "A DivideByZeroException error occured" -ForegroundColor Red
  }
  Catch {
    Write-Host "Some other error occured" -ForegroundColor Red
  }
  Finally {
    "This part always executes, even if an error occurs."
  }
}

First, let’s generate an error of the type PSInvalidCastException.


PS > TryCatchError -Command { [int]1 + [string]"Hello" }

A PSInvalidCastException error occured
This part always executes, even if an error occurs.

Next we try to divide 1/$null.


PS > TryCatchError -Command { 1/$null }

A DivideByZeroException error occured
This part always executes, even if an error occurs.

If we try to create an instance of a .NET Object and specify a nonexisting class, the error is handled as expected.


PS > TryCatchError -Command { New-Object System.Blah }

Some other error occured
This part always executes, even if an error occurs.

Now let’s try Get-ChildItem and specify a nonexisting folder.


PS > TryCatchError -Command { Get-ChildItem C:\Blah }

Get-ChildItem : Cannot find path 'C:\Blah' because it does not exist.
At line:1 char:39
+ TryCatchError -Command { Get-ChildItem <<<<  C:\Blah }
    + CategoryInfo          : ObjectNotFound: (C:\Blah:String) [Get-ChildItem], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

This part always executes, even if an error occurs.

The Error is handled by the Get-ChildItem cmdlet rather than our Catch statement. Setting the ErrorAction parameter to Stop lets our Catch statement handle the error instead.


PS > TryCatchError -Command { Get-ChildItem C:\Blah -ErrorAction Stop }

Some other error occured
This part always executes, even if an error occurs.

Rating 4.00 out of 5
[?]