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.
- By Chris Brooke
- 06/01/2000
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].