Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use try... catch and get my script to stop if there's an error?

Tags:

powershell

I'm trying to get my script to stop if it hits an error, and use try... catch to give me an easy way to handle the error. The easiest thing in the world I'd have thought, but I'm obviously doing something stupid. I have read for a couple of hours and I'm stuck, any help would be very handy, thanks!

Here's some example code, I've put erroraction all over the place, can't seem to stop the damn thing!

$ErrorActionPreference = "Stop"
try {
    get-content "c:\GarbageFileName.txt" -ErrorAction stop
}
catch {
    write-output "in catch, I want it to stop now"
}
write-output "try-catch finished, script is continuing"

this text added next day * Fantastic answers from people, I wish I could choose more than 1 answer or had enough reputation to vote for the ones which I reluctantly didn't choose as the answer, or say thanks somehow!

like image 475
Aberdeen Angus Avatar asked Oct 09 '13 21:10

Aberdeen Angus


People also ask

Can we handle error using try catch?

The try statement defines a code block to run (to try). The catch statement defines a code block to handle any error. The finally statement defines a code block to run regardless of the result. The throw statement defines a custom error.

How do you handle errors in catch block?

You can put a try catch inside the catch block, or you can simply throw the exception again. Its better to have finally block with your try catch so that even if an exception occurs in the catch block, finally block code gets executed. Finally block may not get executed in certain exceptions.

What is try catch error handling?

What Does Try/Catch Block Mean? "Try" and "catch" are keywords that represent the handling of exceptions due to data or coding errors during program execution. A try block is the block of code in which exceptions occur. A catch block catches and handles try block exceptions.

How do you handle errors in PowerShell script?

Use the try block to define a section of a script in which you want PowerShell to monitor for errors. When an error occurs within the try block, the error is first saved to the $Error automatic variable. PowerShell then searches for a catch block to handle the error.


3 Answers

The whole idea of a try/catch control is that you tell the script what to do if it encounters a terminating error, instead of the default action of throwing the error and stopping the script. If your catch block just displays a message to the terminal with Write-Host, that's all the error handling there will be, and the script will continue from there. If you think about it, it would partially defeat the purpose of try/catch if the script were stopped automatically whenever an error is caught.

In the catch block, $_ will be set to the ErrorRecord object representing the terminating error from the try block (the same one that gets stored in $error[0]). So the simplest way to end the script is to rethrow the error that would have been thrown if you hadn't used a try/catch:

try {
  Get-Content "c:\GarbageFileName.txt" -ErrorAction stop
} catch {
  # Custom action to perform before terminating
  throw $_
}

Or, if you want to display a custom message instead of the default ErrorRecord:

try {
  Get-Content "c:\GarbageFileName.txt" -ErrorAction stop
} catch {
  throw 'Custom error message'
}

Or you could use break as suggested in Joost's answer if you want to just quit after you're finished with your custom error handling without throwing an error to the error stream.

Or you could get more sophisticated and create your own ErrorRecord object. There's a lot you can do with that, it's too big a topic to cover comprehensively here, but you can get more info about the syntax by googling System.Management.Automation.ErrorRecord. Here's an example from one of my scripts to get you started (from a function that executes a SQL query defined in the $query variable against a SQL Server database):

} catch {
  $ErrorRecord = New-Object System.Management.Automation.ErrorRecord(
    (New-Object Exception("Exception executing the query: $($_.Exception.InnerException.Message)")),
    $query,
    [System.Management.Automation.ErrorCategory]::InvalidArgument,
    $null
  )
  $ErrorRecord.CategoryInfo.Reason = $_.CategoryInfo.Reason;
  $ErrorRecord.CategoryInfo.Activity = $_.InvocationInfo.InvocationName;
  $PSCmdlet.ThrowTerminatingError($ErrorRecord);
}

A couple of notes:

  • You'll see that in creating my custom ErrorRecord, I'm using $_, which I just said contains the ErrorRecord object associated by the terminating error that was caught in the try block. The idea is to customize some of the error output, while using parts of the default ErrorRecord by assigning them to the corresponding properties for the custom ErrorRecord.
  • $PSCmdlet is only available if you declare [CmdletBinding()] at the beginning of the function or script. Otherwise, you can just use throw $ErrorRecord to throw your custom error. However, the result will be more Cmdlet-style if you use $PSCmdlet.ThrowTerminatingError. (throw will spit back the line from the function that generated the error, whereas $PSCmdlet.ThrowTerminatingError will give you the line from the calling context where the function was used. It's hard to describe in a way that makes sense without getting too elaborate, but if you experiment with it you'll see what I mean.)

BTW, it's redundant to set $ErrorActionPreference = "Stop", and then use -ErrorAction Stop. The preference variable sets the default action for all cmdlets, and the -ErrorAction switch overrides the default action for a particular cmdlet, so there's no need to first specify the default, then use -ErrorAction to specify the same action you just set as the default. What you probably want to do is just leave out $ErrorActionPreference = "Stop".

like image 146
Adi Inbar Avatar answered Nov 12 '22 19:11

Adi Inbar


The only thing missing is your break-statement in the Catch-block. Powershell won't stop the script if you don't instruct it to.

try {
    get-content "c:\GarbageFileName.txt" -ErrorAction stop
}
catch {
    write-output "in catch, I want it to stop now"
    break
}
write-output "try-catch finished, script is continuing"

And a small addendum, in case it helps you: with finally, you can add some lines of code that are always executed, regardless of wether an exception was thrown or not.

try {
    get-content "c:\GarbageFileName.txt" -ErrorAction stop
}
catch {
    write-output "in catch, I want it to stop now"
    break
}
finally {
    #do some stuff here that is executed even after the break-statement, for example:
    Set-Content -Path "f:\GarbageFileName.txt" -Value $null 
}

#the line below is executed only if the error didn't happen
write-output "try-catch finished, script is continuing"
like image 22
Joost Avatar answered Nov 12 '22 19:11

Joost


Try-Catch will catch an exception and allow you to handle it, and perhaps handling it means to stop execution... but it won't do that implicitly. It will actually consume the exception, unless you rethrow it. But your issue is simpler than that -- the try block takes precedence over the -ErrorAction stop in your get-content cmdlet. So instead of stopping execution, you get taken to the Catch block and continue on because there is no error handling in the catch block.

Try removing the try-catch logic from your script, and allow the cmdlet to error out:

get-content "c:\GarbageFileName.txt" -ErrorAction stop
write-output "You won't reach me if GarbageFileName doesn't exist."

And you should get the desired result of execution not reaching write-output:

PS C:\> .\so-test.ps1
Get-Content : Cannot find path 'C:\GarbageFileName.txt' because it does not exist.
At C:\so-test.ps1:2 char:12
+ get-content <<<<  "c:\GarbageFileName.txt" -ErrorAction stop
    + CategoryInfo          : ObjectNotFound: (C:\GarbageFileName.txt:String) [Get-Content], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
like image 37
Anthony Neace Avatar answered Nov 12 '22 19:11

Anthony Neace