none
Yet another Try Catch question. Iterating through a ForEach loop RRS feed

  • Question

  • Confused on error handling in a Powershell ForEach loop. I’m looping through a list of registry keys, attempting  to open each one. If it succeeds, I do a bunch of stuff. If it fails, I want to skip to the next iteration.

    If I was doing It in VBScript I’d do this:

    For Each Thing In colThings
    Open Thing
    	If Err.Number <> 0 Then
    	    “oops”
    	Else
    	    Do stuff
    	    Do stuff
    	    Do stuff
    	End If
    Next
    

    This is what I came up with in PowerShell. It seems to work, but just doesn’t seem powershell-ish. There must be a better way to use the catch output than just creating a $return variable and assigning it success or fail?

    ForEach ($subKeyName in $subKeyNames)
    {
    try{$subKey = $baseKey.OpenSubKey("$subKeyName")}
    	catch{$return = "error"	}
    
    	If($return -eq "error" )
    	{
    	    “Oops”
    	}
    	Else
    	{
      	    Do stuff
    	    Do stuff
    	    Do Stuff
    	}
    }
    


    Wednesday, April 9, 2014 6:59 PM

Answers

All replies

  • It all depends on what you want to do with the error condition.  There is no one rule.

    I would start by formatting your script to be more standard.

    ForEach ($subKeyName in $subKeyNames){
        try{
             $subKey = $baseKey.OpenSubKey($subKeyName)
      	    Do stuff
    	    Do stuff
    	    Do Stuff
         }
         catch{
              $_
         }
    }

    This the basic way we use try/catch in PowerShell, C, C++, C#, VB.Net and many other languages.  We wrap all of a group o lines in a "Try".  THis allows the script to abort a set of steps cleanly on the first error.  Design of exception handling is a whole area of programming.  Search for documents and books that discuss how it works.  It is not just a set of simple lines of code.

    Don't let yourself get into a bad habit of putting quotes around everything.  It will cause no end of trouble.

    Start with one of the many good books on PowerShell and try and master the basics before jumping heavily into advanced topics.  They will make more sense if you know the basic well.


    ¯\_(ツ)_/¯

    Wednesday, April 9, 2014 7:10 PM
  • Here is one basic explanation that is not too bad or complicated.

    http://blogs.msdn.com/b/kebab/archive/2013/06/09/an-introduction-to-error-handling-in-powershell.aspx


    ¯\_(ツ)_/¯

    Wednesday, April 9, 2014 7:17 PM
  • The decsion of how many lines to put in the Try block can be subtle.  It depends on what you intend to do when an error occurs.  For this example, what JRV suggested is perfect, because you said you just want to skip the rest of the statements and move on to the next loop iteration, regardless of what the error was or what statement caused it.

    In other situations, it's better to have very small, focused try/catch blocks that handle specific errors from a small number of commands.  (These small try/catch blocks might be nested into a much larger try block, if you want the best of both worlds.)  It's one of those topics that people sometimes feel very strongly about, but that doesn't always have a right or wrong answer.

    Wednesday, April 9, 2014 7:18 PM
  • Here is a taste of the issues and variability surrounding Try/Catch and error management in general:

    http://powershell.org/wp/2013/06/11/powershell-great-debate-error-trapping/


    ¯\_(ツ)_/¯

    Wednesday, April 9, 2014 7:19 PM
  • David is pointing us towards the great debate surrounding error management.  There is no absolute answer but there are many guidelines which all programmers need to be aware of as they can save a lot of coding and headaches.

    I have been in try/catch debates since about 1995 or so.  THe C/C++ community overhalled the ANSI standaards and nailed down try/catch behavior as much as possible.  Microsoft at that time released compilers with and OS updates with full SEH (Structured Exception Handling) capability.  Try/Catch is the tip of a very big iceberg that manages exceptions from the hardware up in Windows in one neat predictable way.

    If you are interested here are the SEH docs at MSDN: http://msdn.microsoft.com/en-us/library/windows/desktop/ms680657(v=vs.85).aspx


    ¯\_(ツ)_/¯

    Wednesday, April 9, 2014 7:26 PM
  •  

    I totally get what you're saying about formatting. I don't' have any habits yet, since I've only been working in Powershell since... well, what time is it now?

    Unfortunately, It Has Been Decreed that we are no longer to use VBScript for any engineering solutions at work, so my 15 years experience in it now needs to be transitioned over asap. I don't have the luxury of crawling before I run. I'm trying not to be frustrated, but it's like an English major waking up one day and being told "You must now speak French exclusively. Here's a book."

    The Do Stuff example of my ForEach loop is about 50 lines of code involving matching values in subkeys of this registry key with another and collecting output. I tried wrapping the whole thing in a try section based on some examples, but it seemed odd, that's why I'm asking. I'm used to tightly focused error handling at the point where an error may occur.

    In this example I'm only interested in whether or not I can open the subkey (it exists, but I may not have permission). If I can't, there's no point in continuing with this iteration of the loop, I want to skip to the next one. So why include all the "Do Stuff" in the the try section? From a readability viewpoint, it doesn't seem helpful.

    Also, there may be more error handling deeper in the code. If I then put that in a try/catch, and then something else inside that, now I have nested try/catches mixed in with nested if/elses, all wrapped in a For loop.

    Again, I can see how it works logically, but for readability not so much, and having all these braces 50 lines apart to match up is giving me eye strain :).

    It sounds like David is agreeing with jrv, that putting the entire ForEach loop code into a try/catch is the conventional way to do it. I guess it makes as much sense as putting it all in an If-else-Endif, and I just need to adjust my paradigm.

    But if not, my specific question was more along the lines of, is there a built in way to tell that the catch section has been executed, rather than me using it to populate an arbitrary variable and then read it? In VBScript, you execute something, and the next line, you check the Err.number. I wasn't sure if you could do that with a try/catch.


    • Edited by JoeZeppy Thursday, April 10, 2014 12:38 AM
    Thursday, April 10, 2014 12:36 AM
  • You can still use a small try/catch block in the loop here, if you like. Just call the "continue" statement from the catch block:

    foreach ($subKeyName in $subKeyNames)
    {
        try
        {
           $subKey = $baseKey.OpenSubKey($subKeyName)
        }
        catch
        {
            Write-Error -ErrorRecord $_
            continue
        }
    
        Do stuff
        Do stuff
        Do Stuff
    
    }

    The end result is the same; it just depends on what your coding guidelines or personal preferences are.

    I've gone through the "VBScript to PowerShell" conversion myself over the last year.  There's definitely a learning curve, getting accustomed to the pipeline, .NET Framework, etc, but once you get the hang of it, you'll never look back.  Maintaining our existing VBScripts drives me crazy now; I convert them to PowerShell anytime I have an excuse to do so.

    • Edited by David Wyatt Thursday, April 10, 2014 1:56 AM
    Thursday, April 10, 2014 1:53 AM
  • How you deploy a try/catch is not really a matter of preference.  In most cases it is driven by the requirements.

    If I have 5 steps that must occur in an order but where any one step that fails means all will fail we would want to quit the processing.  We wold not want to continue to throw exceptions for no reason..

    Try/Catch blocks can be nested.

    Functions make convenient containers for try/catch.

    Function MyFunc{
         Try{
              # some group of required, dependent steps.
              return $true
        }
       Catch{
             # log error if needed
             return $false
       }
    }

    As in the link I posted above.  There are design patterns that can be described that model common usage of Try/Catch.

    One thing that is not mentioned much with scripting is the concept of "transactions".  PowerShell has some support for transactions.  Fro example, the Microsoft Installer is fully transacted and works well with exceptions.  An Exception can be used to "roll back" all steps that have occurred if any step fails.

    An exception can be used to undo actions when a step fails this preventing an incomplete system update.

    The knowledge of how to successfully design with exceptions comes with experience.


    ¯\_(ツ)_/¯




    • Edited by jrv Thursday, April 10, 2014 2:45 AM
    Thursday, April 10, 2014 2:39 AM
  • Don't forget the other very important reason for designing with SEH.

    try
    {
       $wc = new-object System.Net.WebClient
       $wc.DownloadFile("http://www.contoso.com/MyDoc.doc")
    }
    catch [System.Net.WebException],[System.IO.IOException]
    {
        "Unable to download MyDoc.doc from http://www.contoso.com."
    }
    catch
    {
        "An error occurred that could not be resolved."
    }

    .... the ability to have multiple exception traps.

    Notice how it handles all possible exceptions and catches some exceptions as known cases.  This also shows how we avoid an attempt to download if the client cannot be created.  This is actually a weak example since this would never happen in real life.

    This  example also shows the second most popular method of indenting.  I tend to use the traditional method for a number of very good reasons including that my editor supports that method correctly and it makes the code segments more visible on the screenas well as highlighting brace pairs.

    A pair of braces should not be separated by two many lines.  If the code  block gets too long it may be a good idea to en-function it.  Ina function we can more easily escape from the block on an error.

    This is where code design skill come in. PowerShell was designed to encourage and assist good code design.  VBScript is too but uses much older models which tend to create much more code.  This also why you do not want to just use VBScript coding styles in PowerShell.  It wouldlead to bloat and confusion although it can be done.


    ¯\_(ツ)_/¯

    Thursday, April 10, 2014 2:57 AM