Automating the world one-liner at a time…
Did your command or script fail and/or report an error? We hope to have a proper script debugger in a future version, but until then, MSH has some handy features to help you figure out what went wrong. In this series of blog entries, I will present some of those features. Thanks to Jim Truher [MSFT], Bruce Payette [MSFT], and Jeff Jones [MSFT] for their help in putting this together.
See the Windows "Monad" Shell Beta 2 Documentation Pack (http://www.microsoft.com/downloads/details.aspx?FamilyID=8a3c71d1-18e5-49d7-952a-c55d694ecee3&displaylang=en) for general information about Monad.
Part 1: http://blogs.msdn.com/monad/archive/2005/11/04/489138.aspx (Terminating vs. Non-Terminating Errors, ErrorRecord)Part 2: http://blogs.msdn.com/monad/archive/2005/11/08/490130.aspx ($error)Part 3: http://blogs.msdn.com/monad/archive/2005/11/09/490625.aspx (write-host)Part 4: http://blogs.msdn.com/monad/archive/2005/11/09/491035.aspx (set-mshdebug)Part 5: http://blogs.msdn.com/monad/archive/2005/11/11/491967.aspx (Preferences and Commandline Options)Part 6: http://blogs.msdn.com/monad/archive/2005/11/15/492769.aspx (Trace-Expression, Breakpoint Script)
Jon Newman [MSFT]
How Traps Work
You can use the "trap" language feature to manage terminating errors in your script. "Getting Started.doc" and "MonadScripting-HandsOnLab.doc" from the Documentation Pack contain some details, but here is a little more detail.
If you don't use "trap" at all, terminating errors halt the command which is executing, and then the script moves on to the next command. In this example, you can see that the "terminating error" only terminates the command where it occurs, and execution continues with the next command, "ending a".
MSH C:\temp> get-content .\test.mshfunction a{write-host "starting a"write-host (1 / $null)write-host "ending a"}write-host "starting main"awrite-host "ending main"MSH C:\temp> .\test.mshstarting mainstarting aAttempted to divide by zero.At C:\temp\test.msh:4 char:4+ 1 / <<<< $nullending aending mainMSH C:\temp> get-content .\test.msh
You can add "trap" statements to your script to handle terminating errors. Inside the trap statement, "$_" is the ErrorRecord for the error, and you can use "break;" to break script execution, or "continue;" to continue with the next command after the one which hit the error.
MSH C:\temp> get-content .\test.mshfunction a{trap [System.DivideByZeroException] { write-host ("trapped " + $_.Exception.GetType().Name); continue; }write-host "starting a"write-host (1 / $null)write-host "ending a"}write-host "starting main"awrite-host "ending main"MSH C:\temp> .\test.mshstarting mainstarting atrapped DivideByZeroExceptionending aending mainMSH C:\temp>
Here's the tricky bit: If the current scope does not contain a trap, but any outer scope does, script execution after a terminating error will break out to the nearest scope where any trap (whether it matches the error or not) is defined. In this example, we move the trap statement out of "function a" and into the outer scope:
MSH C:\temp> get-content .\test.mshtrap [System.DivideByZeroException] { write-host ("trapped " + $_.Exception.GetType().Name); continue; }function a{write-host "starting a"write-host (1 / $null)write-host "ending a"}write-host "starting main"awrite-host "ending main"MSH C:\temp> .\test.mshstarting mainstarting atrapped DivideByZeroExceptionending mainMSH C:\temp>
Note that "ending a" doesn't appear in the output; execution broke out of function a.
This applies whether or not the error which occurred matches the exception you are trapping:
MSH C:\temp> get-content .\test.mshtrap [System.ArgumentException] { write-host ("trapped " + $_.Exception.GetType().Name); continue; }function a{write-host "starting a"write-host (1 / $null)write-host "ending a"}write-host "starting main"awrite-host "ending main"MSH C:\temp> .\test.mshstarting mainstarting aAttempted to divide by zero.At C:\temp\test.msh:6 char:4+ 1 / <<<< $nullending mainMSH C:\temp>
You may ask, why is this? Remember that trap statements operate in the scope in which they are declared. We had implementation and performance problems in exiting the "function a" scope, running the trap block in the outer scope, then returning to the "function a" scope.
[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]
So why do you care about debugging powershell scripts? Umm unless you have the superhuman ability to