Functions not only help you organize your scripts into subroutines, they also let you choose between going global or staying local.
Get Yourself Organized
Functions not only help you organize your scripts into subroutines, they also let you choose between going global or staying local.
- By Chris Brooke
- 07/01/2000
One of the most important aspects to scripting is keeping
your scripts organized. The best way to do this is to
separate the various functions into different Functions
(and Subs). Last month we got our first real taste of
user-defined procedures when I used Subs to separate the
functionality of my ResetPW.vbs script. I hope I didn’t
leave you too shell-shocked, since I did kind of spring
it on you all of a sudden. This month we’re going to take
a closer look at Subs and Functions and see if we can’t
clear up a few things.
Subs and Functions
Putting different functionality into different sub-routines
is nothing new. It ensures that the logic of your scripts
is easy to trace; it also reduces potential errors. The
most organized way we have of, well… organizing our scripts
is through the use of Subs (short for either subroutine
or subprogram, depending on who you ask) and Functions.
Subs and Functions are essentially the same with one exception:
a Sub does not return a value (at least not directly),
a Function does. Let’s use a simple script to illustrate
the difference between Subs and Functions.
'Hello World! w/ Sub
Call HelloWorld
Sub HelloWorld
WScript.Echo "Hello World!"
End Sub
'Hello World! w/ Function
Dim strHello
strHello=HelloWorld
WScript.Echo strHello
Function HelloWorld
HelloWorld="Hello World!"
End Function
The output of both scripts is simply the display of “Hello
World!” Sometimes, however, it’s important to have a value
returned to the script. This is where a Function can come
into its own.
Conjunction Junction, What’s Your Function?
The ability to return a value to the script can be quite
important. In the simplest case, this can be merely a
Boolean value (true/false, yes/no). Let’s use a built-in
VBScript Function—MsgBox—to demonstrate this:
'MyFunction.vbs
Dim bAnswer 'Yes for OK, No for Cancel
bAnswer=MsgBox("Click to continue",
vbOKCancel, "Continue?")
If bAnswer=False Then WScript.Quit
Rest of script
The MsgBox Function displays a window with “Click
to continue” and two buttons: “OK” and “Cancel.” If the
user clicks OK, the function returns a “True” value and
the script continues. If the user clicks Cancel or hits
Escape, the function returns a “False” value and the script
terminates. Of course, you can set up user-defined Functions
to return whatever value you wish.
The Tip of the Iceberg
Keeping things organized is only one advantage to using
Subs and Functions. One of the most useful features inherent
to procedures is that any variables declared within a
procedure are local to that procedure only. They can’t
be used by the rest of the script. Up to this point, we’ve
always placed our variable declarations at the beginning
of the script. Variables declared in this way are available
to the entire script—procedures and all. There will be
times, however, when you’ll want to keep certain variables
local to the procedure in which they’re used.
'KeepEmLocal.vbs
Option Explicit
Dim strHello
strHello="Hi!"
WScript.Echo strHello
Hello1
Hello2
WScript.Echo strHello
WScript.Quit
Sub Hello1
Dim strHello
strHello="Hello World!"
WScript.Echo strHello
End Sub
Function Hello2
Dim strHello
strHello="Hi Ya'll"
WScript.Echo strHello
End Function
Figure 1 shows the output from this script.
|
Figure 1. Keeping the variable
inside the procedures keeps its value local to the
subroutine. (Click image to view larger version.) |
As you can see, by declaring the variable inside the
procedures, we’ve kept its value local to those Subs.
The changes I made to strHello inside the Sub Hello1 and
the Function Hello2 had no effect on the value of strHello
that was declared in the main body of the script. Now,
I know what you’re thinking… “strHello was first declared
in the main body of the script and should have been available
to all the Subs and Functions.” It was and it is. But,
since we re-declared strHello inside the procedures, two
new variables were created with the same name and are
private to those procedures. I’ll prove it. Add another
Sub to the script called Hello3.
Sub Hello3
WScript.Echo strHello
End Sub
Insert a call to Hello3 right after Hello2. Run the script
again and the output looks like Figure 2. Hello3 was able
to use the original value of strHello (“Hi!”) that was
set at the beginning of the script because it wasn’t independently
declared inside the sub! If you don’t declare the variable
inside the procedure, VBScript assumes that you want to
use a “global” variable.
|
Figure 2. In this script, Hello3
uses the original value of strHello ("Hi!").
(Click image to view larger version.) |
This “variable independence” that Subs and Functions
possess also extends to other areas. You’ll recall in
last month’s column that the On Error Resume Next statement
was also able to be used independently within procedures,
as well as globally.
This Is All Neat and Everything, But…
Let’s take a look at how we can apply this knowledge
in some practical way to our scripts:
'KeepEmLocal.vbs
Option Explicit
Dim objFSO, objFile
Set objFSO=CreateObject
("Scripting.FileSystemObject")
Set objFile=objFSO.GetFile
("c:\MyFirstFile.txt")
WScript.Echo objFile.Name
MySub
WScript.Echo objFile.Name
WScript.Quit
Sub MySub
Dim objFSO, objFile
Set objFSO=CreateObject
("Scripting.FileSystemObject")
Set objFile=objFSO.GetFile
("c:\MySecondFile.txt")
WScript.Echo objFile.Name
MySub2
Set objFSO=Nothing
Set objFile=Nothing
End Sub
Sub MySub2
WScript.Echo objFile.Name
End Sub
As you can see from the script and in Figure 3, keeping
variables private also extends to objects. In the above
script we create the same object twice—once globally,
once within a Sub. In line 8 we echoed the first file
name to the screen. We then created a second object (with
the same name) in MySub. We echoed its name to the screen,
then called MySub2 to do it again. However, MySub2 echoed
back the MyFirstFile.txt instead of MySecondFile.txt because
it was declared globally!
|
Figure 3. The concept of keeping
variables private extends to objects. (Click image
to view larger version.) |
I’m way ahead of you. You’re about to ask “Why not just
create another instance of the FileSystemObject with a
different variable name, like objFSO2?” The answer lies
in the root of what makes procedures valuable in the first
place: Reusability! Now that we’ve established the fundamentals
of Subs and Functions and how they keep variables private,
I want you to write a script to use the same procedure
(using only one object!) to compare the “DateLastModified”
property (hint: use the FileSystemObject) of two files.
I’ll have my version for you next month, as well.
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].