Exception handling in Powershell is primarily based on try-catch-finally and trap. Let’s see some scenarios,
function throws-error { throw "an error occured here" }
throws-error # unhandled exception - script will error out
If we want to catch an error, we can use the try-catch construct,
function throws-error {
# we can throw a .NET exception too
throw [System.IO.FileNotFoundException] "throwing a filenotfoundexception"
}
try { throws-error }
catch {
# let's get some more information about the error
write-host $_
$_.GetType().FullName # the type of $_ is 'System.Management.Automation.ErrorRecord'
$_.Exception
$_.Exception.GetType().FullName # the exception type name is 'System.IO.FileNotFoundException'
$_.Exception.Message # the exception message
}
Just like in C#, we can catch different types of exceptions with a single try. Order is important, catch the most specific exceptions first.
function throw-errors([int]$errorType) {
switch ($errorType) {
1 { throw [System.IO.FileNotFoundException] "throwing a filenotfoundexception" }
2 { throw [System.IO.IOException] "throwing a ioexception" }
3 { throw [System.SystemException] "throwing a systemexception" }
default { throw [System.Exception] "a default error" }
}
}
try { throw-errors 2 }
catch [System.IO.FileNotFoundException] { "caught filenotfoundexception" } # catch the most specific exception first
catch [System.IO.IOException] { "caught ioexception" }
catch [System.SystemException] { "caught systemexception" }
catch [System.Exception] { "caught exception" } # catch the most generic exception last
finally { "done" }
Every pre-defined Powershell cmdlet comes with built-in error handling via a common parameter called –ErrorAction. To see this is action try the commands below,
get-process –fileversion # you'll get a few errors in the output
get-process -fileversion -erroraction silentlycontinue # ignore the errors
get-process -fileversion -ea 0 # 'ea' is the alias for erroraction
get-process -fileversion -errorvariable errors # get an array of errors that occured
write-host $errors.count # how many errors occured?
foreach ($error in $errors) { write-host "Error:" $error }
If you want to import the common parameters into your function then use the CmdletBinding.
function throws-error {
[CmdletBinding()] # our function now supports the ErrorAction param implicitly
param()
throw "an error occured here"
}
function throws-error2 {
# ErrorAction actually sets the $ErrorActionPreference, and we can do this ourselves
$ErrorActionPreference = "silentlycontinue"
throw "an error occured here"
}
throws-error -ea 0 # no error is thrown
throws-error2 # no error is thrown
Now let’s look at the other Powershell way of handling exceptions – trap. Traps handle the exception and allow script execution to continue.
trap [IO.FileNotFoundException] { "trapped outside " + $_.Exception; continue; }
function test-trap {
# break results in re-throwing the exception, so it can be handled by the outer trap
trap [IO.FileNotFoundException] { "trapped inside " + $_.Exception; break; }
throw [IO.FileNotFoundException] "a filenotfoundexception"
"will not output this line"
}
test-trap
The ‘will not output this line’ doesn’t get output because with ‘continue’, execution is returned to the scope the trap is in and the next command is executed. To see this more clearly,
function test-trap {
trap { "outer " + $_.Exception; continue; }
if($true) {
trap { "inner " + $_.Exception; break; }
throw "an error occured";
"will not output this line"
}
"will output this line"
}
test-trap
We can also trap multiple exceptions, like catch blocks,
function test-trap {
throw [IO.FileNotFoundException] "a filenotfoundexception"
throw (new-object IO.IOException("an ioexception")) # this is executed because of the 'continue' in the filenotfoundexception trap
throw (new-object SystemException("a systemexception")) # note the different way throwing
throw [Exception] "a default error"
# order of traps not important
trap [SystemException] { $_.Exception; continue; } # 'continue' execute the next command in the scope of the 'trap'
trap [IO.FileNotFoundException] { $_.Exception; continue; } # 'continue' causes the throw ioexception statement to execute
trap [Exception] { $_.Exception; continue; }
trap [IO.IOException] { $_.Exception; continue; }
"done"
}
test-trap
As you can see, using traps are a little tricky, using try-catch should cover most (if not all) your cases.