As long as you have users, you're going to have data entry errors. Here's how to write scripts to handle bad data with ease.

Garbage In, Garbage Out

As long as you have users, you're going to have data entry errors. Here's how to write scripts to handle bad data with ease.

GIGO—Garbage In, Garbage Out. It’s a term that’s been used ad nauseam by programming geeks like me for decades. Historically, we’ve thrown it out as a way to justify unpredictable behavior in our applications and scripts. Usually it’s true. There are times, however, when a bit of foresight and common sense can go a long way toward reducing the amount of “noticeable” errors. As long as humans enter data, there will be mistakes. The goal is to recover from these mistakes as transparently as possible.

Last month we took a crash course in error handling. This month we’ll see how we can raise errors intentionally in our scripts. We’ll also look at how VBScript handles internal errors. First, though, I owe you my version of the homework I assigned in last month’s column. As I recall, we were supposed to modify the “ResetPW.vbs” script to let the user correct the garbage in, passed from the command-line, and provide a more elegant recovery than simply terminating the script. Here’s what I came up with:

'ResetPW.vbs
'With error handling

'***** Section 1 *****
Option Explicit
On Error Resume Next
Dim objUser, objArgs
Dim strDomain
Dim iCount, iNum
Set objArgs=WScript.Arguments
Set objUser=CreateObject(
            "SoftArtisans.User")
iNum=0

'***** Section 2 *****
'Start execution
Main

Set objArgs=Nothing
Set objUser=Nothing

WScript.Quit

'***** Section 3 *****
'Main Procedure
Sub Main
   GetArgs
   GetDomain
   ResetPW
End Sub

'***** Section 4 *****
'Get command-line arguments
Sub GetArgs
   If objArgs.Count<>1 Then
      GetManually
   Else
      strDomain=objArgs.Item(0)
   End If
End Sub

'***** Section 5 *****
'Get Domain Controller
Sub GetDomain
On Error Resume Next
objUser.GetDomainController strDomain, True

If Err.Number<>0 Then
   msgbox Err.Description 
      & "Please try again."
   Err.Clear
   GetManually
   GetDomain
End If

   iCount=objUser.UserCount
End Sub

'***** Section 6 *****
Sub ResetPW
   Do
      objUser.User=objUser.UserItem(iNum)
      objUser.MustChangePW=True
      iNum=iNum+1
   Loop Until iNum=iCount
End Sub

'***** Section 7 *****
Sub GetManually
   strDomain=InputBox("Please enter a domain
      controller", "Missing Arguments")
   If strDomain=Empty Then GetManually
End Sub

You’ll notice a different structure to this script than we’ve used thus far. Because we want to be able to resume execution at a previous point (in case an error occurs), it makes sense to structure the script with user-defined procedures (subs). I’ll cover the ins and outs of these procedures in a future column. For now, the basic concept is to divide tasks into sections and execute them individually. I start with a Main Sub (Section 3) that I can always refer back to in case of error. Sections 4, 5, 6, and 7 keep the main logic of the script in separate procedures.

You might also notice that I’ve again used two different types of error handling: the Err object and “logical” error handling. The “logical” part is in Section 4. If the user doesn’t enter a command-line argument to specify the domain, it prompts the user to enter one. I handle this “logically” because command-line arguments aren’t required in a script; therefore, not having them won’t fire the Err object. I actually could have used the Err object to handle this part as well, by simply using Err.Raise. (I’ll get to that in a minute.) However, for the purposes of this procedure, logical error handling is sufficient.

Where the Err object does enter in is in Section 5. If the user enters an invalid or unreachable domain, an error is fired. Then it’s back to square one, asking the user to enter it.

The Err Object

You might be wondering why I put another On Error Resume Next command in Sub GetDomain (Section 5). The reason for this is that VBScript uses a technique called “structured exception handling.” This means that when an error occurs, VBScript resumes execution with the line following the call to the procedure that caused the error. If the only On Error… statement was the one at the beginning of the script, execution would have resumed with the line after Main in Section 2, rather than the next line in Section 5.

In fact, I don’t really need the On Error… statement in Section 1, considering I don’t have any code to handle the error even if it does occur. I left it there in case I modify the script further and have potential error-producing code in the main body of the script. With complex scripts containing nested procedures, figuring out where to continue execution can get complicated. This is VBScript’s way of keeping it simple. Again, this will be clearer when I cover procedures in a future column.

Go Ahead… Make My Error

There are times when you might want to invoke the Err object inside your scripts. This is done with the Err.Raise method. Why would you want to cause an error intentionally in your script? Well, for one thing, it helps you determine if the error processing is working. You can raise specific errors using Err.Raise to ensure that execution continues and error handling works.

You can also use Err.Raise inside finished scripts to invoke the Err object when it normally wouldn’t be. A perfect example of this occurs in Section 4. Rather than handle improper or missing command-line arguments “logically,” you could simply raise an error and resume processing either in the next line of the script (if you put an On Error… statement at the beginning of Sub GetArgs), or with the next line in the procedure that calls GetArgs (or wherever the most recent On Error… statement is). This can be quite helpful if you need to resume execution of the script exactly where you left it in the previous procedure, rather than going back to the beginning (as you would if you simply called the procedure again).

Mistakes happen. Users try to type too fast (or, like me, can’t type at all!) or simply type the wrong thing. The important thing is to provide a mechanism inside your scripts to prevent them from exploding. After you’ve succeeded in this, you can coin a new phrase: GIDO—Garbage In, Data Out!

About the Author

Chris Brooke, MCSE, is a contributing editor for Redmond magazine and director of enterprise technology for ComponentSource. He specializes in development, integration services and network/Internet administration. Send questions or your favorite scripts to [email protected].

Featured

comments powered by Disqus

Subscribe on YouTube