As everyone in IT knows, sometimes tasks that you think are quick and easy turn out to take longer.
Today I experiences a small example. I have a scheduled PowerShell script that copies a large file. I wanted to enhance the script so that I would receive an email stating whether the copy succeeded and when it completed.
The original script, for the sake of the example (it is actually a little more involved) is this:
Copy-Item somefile somefile.bak
"Clean up"
where “Clean up” represents some code that runs after the copy completes.
Now, by default a PowerShell may continue if it hits an error other than a syntax error. Again for the sake of the example, I will add a line which echoes text to represent the code that send an email. If you run this script:
Copy-Item somefile somefile.bak
"Clean up"
”Send email: File copied successfully”
then the email gets sent whether or not the copy succeeds. Here is what happens if somefile does not exist:
Not much use. The solution, I thought, was to use PowerShell’s trap statement. This catches the exception you specify, or all exceptions, and runs some code. You can also use the break or continue keywords to determine whether the block of code in which the exception occurred continues with the next statement, or exits immediately. You can read about trap here. I therefore wrote a script that looks like this:
Function CopyIt {
trap {return "Error copying file: $_"; break;}
Copy-Item somefile somefile.bak
return "Copy successfully completed"
}$result = CopyIt
"Clean up"
”Send email: $result”
What I could not figure out though was that even when the copy failed, the script still sent me a success message:
I assumed I was misunderstanding how trap works. The output shows, in this example, that an ItemNotFoundException is thrown, so the trap should be triggered, right?
Wrong. In the case of non-terminating errors (PowerShell jargon) you have to specify the ErrorAction. The default ErrorAction is “Continue”. Therefore I had to modify the script like this:
Copy-Item somefile somefile.bak -ErrorAction stop
Now I get an email with a failure message if the copy fails:
All very obvious to PowerShell experts; but it seems to me that PowerShell makes it easy for the unwary to write scripts that fail silently. You are unlikely to see the red text when your script is running on a server.
If you are concerned with errors in powershell I would suggest setting the global $ErrorActionPreference. That way any errors will fail your execution and you need to either trap or catch everything.
I on the other hand prefer the continue approach as it seems most of the time more useful than having your entire script bail because of a lost connection to the DB or a failed SMTP attempt.
Yes, it is OK once you have figured out how it works. Simply put the stop ErrorAction on any critical operations.
Tim
This is a good article, but in my opinion, you really need to look at the differences between terminating and non-terminating errors. I believe Bruce Payette discusses $ErrorActionPreference in his Manning.com book, and you may be mistaken in assuming it will handle terminating errors also. The ErrorAction parameter is very useful, but one has to make sure to pass it a “Stop” value to force all errors to be terminating ones, then you can use a catch.
On that last comment:
“…then you can use a catch”.
Should be:
“…then you can use a try/catch”.