|
Chapter 2: VBScript Primer
Chapter 2 VBScript PrimerMicrosoft® Visual Basic® Scripting Edition (VBScript) is an easy-to-use scripting language that enables system administrators to create powerful tools for managing their Microsoft® Windows®-based computers. In the first half of this chapter, the fundamental principles of VBScript are illustrated by the creation of a simple script for determining the amount of free disk space on drive C of a computer. This script will evolve throughout the chapter into a more sophisticated tool, one that can determine the amount of free space for any drive on any computer. The second half of this chapter then explores these fundamental principles of VBScript programming in more detail and touches on other VBScript constructs of interest to system administrators.
VBScript OverviewMicrosoft® Visual Basic® Scripting Edition (VBScript) is often dismissed as being "just" a scripting language, the implication being that a scripting language is of little use to a system administrator faced with managing hundreds or even thousands of computers in an enterprise setting. Yet nothing could be further from the truth; when used in combination with technologies such as Windows Script Host (WSH), Windows Management Instrumentation (WMI), and Active Directory Service Interfaces (ASDI), VBScript becomes a powerful language for creating system administration tools. For example, using VBScript in combination with WMI and ADSI, you can write a script of 10,000 or so lines, complete with error handling, subroutines, and other advanced programming constructs. That single script can give you complete control over many aspects of your computing environment.But what makes VBScript such a useful tool for system administrators is that you do not have to create such elaborate and complicated solutions. You can instead spend a few minutes typing a handful of lines of code into Notepad, and instantly create a custom solution to a particular problem. For example, the three-line script in Listing 2.1 tells you how much free disk space is available on drive C of your computer. Listing 2.1 Retrieving Free Disk Space Using VBScript
1 Set objWMIService = GetObject("winmgmts:")If you have been having problems with users filling up drive C on their computers, you now have a custom solution for identifying the computers running low on disk space, a solution developed using nothing more sophisticated than Notepad. Of course, it might be that this script does not fully address your needs. For example, the script tells you the free space available only on your local computer; it cannot tell you how much free space is available on a remote computer. Likewise, the script reports the free space available only on drive C; it tells you nothing about free space available on drives D or E. But if the script does not fully meet your needs, it can easily be modified, and without starting from scratch. This is another advantage of scripting in general and VBScript in particular: You can start with a very simple script and add to it as your needs change and as you become more proficient with the language. This chapter illustrates this process. It begins with the script shown in Listing 2.1, which reports the amount of free disk space on drive C. Subsequent sections in the chapter will take this simple three-line script and gradually add functionality to make it more useful in more situations. When this series of enhancements is complete, you will have a script that can:
As new features are added to the script, the VBScript constructs required to add this functionality are briefly explained. After the script has been completed, a reference section will cover these constructs (and others) in more detail.
Working with ObjectsVBScript allows system administrators to create complex scripts using such advanced programming capabilities as branching, looping, error handling, and the calling of functions and subroutines. It does not, however, include intrinsic methods for performing system administration tasks. VBScript has built-in functions for determining the square root of a number or the ASCII value of a character, but no built-in methods for stopping services, retrieving events from event logs, or carrying out other tasks of interest to system administrators.Fortunately, there are other ways to programmatically perform these tasks, primarily through the use of Automation objects. Automation objects are a subset of COM (Component Object Model), a standard way for applications (.exe files) or programming libraries (.dll files) to present their functionality as a series of objects. In turn, programmers (or script writers) can use these objects, and the functionality of the application or programming library, in their own projects. For example, a word processing application might expose its spell checker as an Automation object, thus providing a way for script writers to add spell checking to their projects. The ability to work with Automation objects and to utilize the properties and methods of these objects makes VBScript a powerful tool for system administration. Admittedly, VBScript alone cannot read events from an event log; however, VBScript can use the functionality included within WMI to retrieve such events. VBScript has no intrinsic methods for creating user accounts; however, the language can use the functionality in ADSI to create such accounts. In fact, VBScript is often referred to as a glue language because one of its primary uses is to "glue" objects together. Rather than provide a seemingly infinite number of intrinsic functions devoted to system administration, VBScript instead provides a framework for using the methods and properties of Automation objects designed to carry out these tasks. For example, the script in Listing 2.2 illustrates the importance of Automation objects within VBScript. This script reports the amount of free disk on drive C of the local computer. Furthermore, it does this using very little VBScript code. Instead, the script:
As noted, there is very little VBScript code here. Instead, the primary purpose of VBScript in this example is to glue together the functionality of WMI and WSH. Listing 2.2 Using Objects in VBScript
1 Set objWMIService = GetObject("winmgmts:")Connecting to Objects Before you can do anything with the data in a database, you must first make a connection of some kind to that database. Likewise, before you can do anything with the methods or properties of an Automation object, you must first make a connection to that object, a process known as binding. Binding to objects can be confusing, because VBScript and WSH both provide a GetObject and a CreateObject method for accessing objects. Furthermore, although the implementations are similar, there are some subtle differences that grow in importance as you become more proficient in scripting. These differences are discussed in more detail later in this chapter. For now, use the following rules-of-thumb without worrying about whether you are using the VBScript or the WSH method (although in most cases you will use the VBScript implementation):
Although it is possible (and sometimes required) to bind to WMI or ADSI using CreateObject, using GetObject and a moniker is typically faster and easier.
Connecting to WSH In line 1 of the script in Listing 2.2, the script binds to WMI by using the following code statement:
Set objWMIService = GetObject("winmgmts:")This connects the script to the WMI SWbemServices object. No similar binding string is used to connect to WSH. Instead, the Echo method is called without first binding to WSH. You are already connected to WSH because WSH is required to run a script written in VBScript. Creating an Object Reference With Automation, you do not work directly with an object itself. Instead, you create a reference to the object by using GetObject or CreateObject and then assign this reference to a variable. After the reference has been created, you can access the methods and properties of the object by using the variable rather than the object itself. In the script in Listing 2.2, the GetObject method is used to assign the WMI SWbemServices object to the variable objWMIService. After the assignment has been made, all the properties of the SWbemServices object can be accessed through objWMIService. For example, in line 2 of the script, the Get method is used to retrieve the properties for drive C. Anytime you create an object reference, you must use the Set keyword when assigning the reference to a variable. For example, the following line of code will result in a run-time error:
objWMIService = GetObject("winmgmts:")To create the object reference, you must use the Set keyword like this:
Set objWMIService = GetObject("winmgmts:")Set is a special VBScript statement that is used only when creating an object reference. If you use Set for other purposes, such as assigning a value to a variable, a run-time error will occur. For example, this line of code will fail because no object named 5 can be found on the computer:
Set x = 5 Calling Methods Automation objects allow you to use their capabilities within your scripts. This enables you to create more powerful and more useful scripts than you could create if you were restricted to the functionality of the scripting language. For example, it is impossible to draw a chart or graph by using VBScript alone. Through Automation, however, you can borrow the capabilities of Microsoft Excel and easily add a chart or graph to, say, a Web page. Automation objects typically expose both methods and properties. (However, there is no requirement for them to expose either.) Methods are equivalent to the actions that the object can perform. For example, although the script in Listing 2.2 is only three lines long, it uses Automation to access the methods of two different COM objects and thus performs two different actions. These two methods are:
After you have created a reference to an object, you can call the methods of that object using dot notation. Dot notation is so named because you call a method by typing the name of the variable that references the object, a period (or dot), and the name of the method. (Depending on the method, you might also type method parameters.) Generally, dot notation uses the following syntax:
ObjectReference.MethodName For example, in the following line of code, the SWbemServices Get method call is written in dot notation.
Set objLogicalDisk = objWMIService.Get("Win32_LogicalDisk.DeviceID='c:'")The parts of the SWbemServices Get method call are shown in Table 2.1. Table 2.1 Parts of the SWbemServices Get Method Call
Retrieving Properties Properties are the attributes associated with an object. They are particularly important in system administration scripting because many of the objects you use are virtual representations of actual objects. For example, in line 3 of Listing 2.2, the FreeSpace property is retrieved, using the same dot notation used to call methods.
objLogicalDisk.FreeSpace With WMI, this object reference refers not to some amorphous programming construct but to an actual hard disk within the computer. The FreeSpace property is thus not just a property of an Automation object but also of drive C. In a sense, WMI creates a virtual mirror of an actual physical object. When you retrieve the properties of that virtual mirror, you also retrieve the properties of the physical object.
VariablesThe script in Listing 2.2 works exactly as expected. When run, it reports the amount of free disk space on drive C. That does not mean that the script cannot be improved, however. For example, the FreeSpace property reports the number of bytes available on a drive. Because disk drive space is typically reported in gigabytes, the FreeSpace property often returns a value that is difficult to interpret. For example, Figure 2.1 shows the value reported for a drive with approximately 10 gigabytes of free disk space.Figure 2.1 Free Disk Space Expressed in Bytes Although it might be obvious from a glance that drive C has adequate disk space, it is far less obvious just how much disk space is actually available. System administrators might find it easier to interpret the data returned by this script if the data were reported as megabytes rather than bytes. VBScript includes a full range of mathematical functions that enable you to perform such tasks as converting bytes to megabytes. In addition, VBScript also provides a construct the variable that can be used to store the results of those mathematical equations. In fact, variables provide a way to store any type of data while the script is running. Variables represent portions of memory that are available to the script as it runs. You can think of computer memory, for these purposes, as being a series of cubbyholes. A variable would be one of these cubbyholes, with an identifying label attached. You can store any kind of data in this cubbyhole and VBScript will retrieve the data when it is needed. When you want to reference the data, VBScript simply looks up the memory address, and reports the information stored there. In line 3 of Listing 2.3, a variable named FreeMegabytes is used to store the results of dividing FreeSpace by 10484576 (which converts bytes to megabytes). As soon as line 3 is run, the variable FreeMegabytes takes on the value of this equation. If you need to refer to the number of free megabytes of disk space anywhere else in the script, you do not have to repeat this equation; instead, you can simply reference the variable FreeMegabytes. This is shown in line 4, where the variable is echoed to the screen. Listing 2.3 Using Variables
1 Set objWMIService = GetObject("winmgmts:")When the script in Listing 2.3 runs, a dialog box similar to that shown in Figure 2.2 is displayed. Figure 2.2 Free Disk Space Converted to Megabytes
Formatting Script Output The value 10340.4458007813 (meaning 10,340 megabytes of free space) is probably more meaningful to the typical system administrator than the value 10842824704. However, the numbers after the decimal point are more of a distraction than they are useful information. Fortunately, VBScript provides several different ways to modify script output. For example, the Int function causes VBScript to display only the integer portion of a number, leaving off all digits following the decimal point. The Int function is shown in line 4 of Listing 2.4. Listing 2.4 Formatting Output
1 Set objWMIService = GetObject("winmgmts:")When the script in Listing 2.4 runs, a dialog box similar to that shown in Figure 2.3 appears. The Int function strips away all the digits following the decimal point, leaving only the integer portion of the original value. Additional formatting commands (covered later in this chapter) can be used to add a comma to the output, resulting in the displayed value 10,340. Figure 2.3 Using the Int Function to Format Output
ConstantsIn the script in Listing 2.3, the amount of free megabytes is calculated by taking the value of the FreeSpace property and dividing it by the hard-coded value 1048576. (Hard-coded values such as this are often referred to as literals because they do not stand for something else but literally represent the value.)In a small script such as this (particularly a small script written for your own use), hard-coding literal values usually does not pose much of a problem. However, in a larger script, particularly one used in an enterprise setting, literals can lead to at least two problems. For one thing, in a small script it might be obvious that 1048576 is the value required to convert bytes (the value returned from the FreeSpace property) to megabytes. In a larger script, however, one that includes a number of mathematical equations, this might be less obvious. This is especially true in an enterprise setting, in which multiple administrators might use and modify the same script. You might know what the 1048576 represents, but another administrator charged with modifying the script might not. The fact that scripts often need to be modified raises a second issue. Literal values not only can be confusing but can also require extra work for anyone modifying the script. Suppose this same procedure, converting kilobytes to megabytes, is used five or six times throughout a script. If you later decide to convert the value to gigabytes rather than megabytes, you will have to correctly modify each line of code where the conversion takes place or your script will no longer provide accurate results. One way to work around the problems that can arise from the use of literals is to use constants instead. Constants are similar to variables in that they are places to store data. Unlike variables, however, after a constant has been defined (that is, after it has been assigned a value), it cannot be changed while the script is running. By assigning important items, such as the value required to convert bytes to megabytes, to a constant, you ensure that the value cannot be changed, inadvertently or otherwise. In the script in Listing 2.5, a constant named CONVERSION_FACTOR is defined in line 1 and is assigned the value 1048576. Later in the script (line 4), the number of bytes of free disk space is converted to the number of megabytes of free disk space. Instead of the literal value 1048576, the constant CONVERSION_FACTOR is used. Both equations return the same result; however, the equation in Listing 2.5 is easier to read and understand. Listing 2.5 Using Constants
1 Const CONVERSION_FACTOR = 1048576 Another benefit to using constants is that they can be defined once and then used multiple times throughout the same script. For example, an expanded version of the script in Listing 2.5 might require you to convert bytes to megabytes several times during the running of the script. Rather than using the literal value in each equation, use the constant instead. If you later decide to convert bytes to gigabytes, you will have to change only the value of the constant; you will not have to change the value used in each equation.
StringsAs you write more sophisticated scripts, you will begin to encounter different types of data (a topic covered in more detail later in this chapter). In line 1 of Listing 2.5, for example, numeric data was used to assign the literal value 1048576 to the constant CONVERSION_FACTOR:
Const CONVERSION_FACTOR = 1048576 This line of code works because a numeric value is being assigned to the constant. Anytime you assign a numeric value to a variable or a constant, you simply type the equals sign followed by the value. However, unexpected results occur if you try to assign an alphanumeric value (typically referred to as a string value) using this same approach. For example, the following code sample attempts to assign the string atl-dc-01 to the variable Computer and then echo the value of that variable:
Computer = atl-dc-01 When this script runs, however, the dialog box shown in Figure 2.4 appears. Figure 2.4 Improperly Assigning String Data to a Variable How did the value -1 get assigned to the variable Computer? When VBScript encounters a set of alphanumeric characters that is not surrounded by quotation marks, it assumes that these characters represent the name of a variable. If it sees a "stray" hyphen, it assumes that the hyphen represents a minus sign. As a result, VBScript interprets the line Computer = atl-dc-01 as "The variable Computer is to be assigned the value of the variable atl minus the value of the variable dc minus 01." Because atl and dc are viewed as new variables that have not been initialized, they are assigned the value 0. VBScript thus interprets this line of code as if it were written like this:
Computer = 0 - 0 - 1 As a result, the variable Computer receives the erroneous assignment -1. When you assign a string value to a variable or a constant, you must enclose that value within quotation marks; this is the only way to ensure that VBScript treats the string as an alphanumeric value and not as a variable. For example, the following code sample correctly assigns the string atl-dc-01 to the variable Computer and then echoes the results:
Computer = "atl-dc-01" When this script runs, the dialog box shown in Figure 2.5 appears. Figure 2.5 Properly Assigning String Data to a Variable Strings as Variables Strings are often used to assign values to variables. For example, the sample script in this chapter uses the following code to bind to WMI. (For information about binding to WMI, see "WMI Scripting Primer" in this book.)
Set objWMIService = GetObject("winmgmts:")This code always connects you to the local computer. This is fine unless you are a system administrator responsible for managing a remote computer or two. In that case, you might want a script that can retrieve the free disk space from a remote computer. That would allow you to sit at your workstation and check the available disk space on any of the computers under your control. When you are using WMI, it is possible to connect to a remote computer simply by including the computer name as part of the moniker passed to GetObject. For example, the following line of code binds to the WMI service on the remote computer atl-dc-01:
Set objWMIService = GetObject("winmgmts://atl-dc-01")You can use the preceding code to write a script that binds to this one remote computer. In an enterprise setting, however, you might want a more flexible script, one that can bind to any remote computer. One way to do this is to edit the script every time you run it, replacing one hard-coded computer name with another. A much better approach is to provide a way for the script to accept input as it runs, and thus operate against a computer whose name has been entered as a command-line argument. User input methods will be discussed later in this chapter. Before that discussion takes place, however, it is important to understand how string values (such as computer names) can be assigned to a variable, and then used as part of the script code. For example, in line 2 of Listing 2.6, the string value "atl-dc-01" is assigned to the variable Computer. In line 3, that variable is used to bind to the WMI service on the computer atl-dc-01. However, this is not done by hard-coding the value atl-dc-01 into the code, but instead by using the value of the variable Computer. Listing 2.6 Using Strings
1 Const CONVERSION_FACTOR = 1048576 In a small demonstration script such as this, assigning the string to a variable actually requires more effort than hard-coding the value. However, this script does illustrate an important concept: You can assign a value to a variable, and then use that variable in place of a hard-coded value. Why is that important? Imagine that this script was designed to retrieve free disk space from 100 computers. Instead of hard-coding separate WMI binding strings for each computer, you could create a single binding string using the variable Computer. Your script could then run that single line of code 100 times, each time replacing the value of Computer with a different computer name. For the moment, however, you have to focus only on line 3 of Listing 2.6:
Set objWMIService = GetObject("winmgmts://" & Computer) Here is how VBScript interprets that line of code:
Set objWMIService = GetObject("winmgmts://"
Set objWMIService = GetObject("winmgmts://atl-dc-01"
Set objWMIService = GetObject("winmgmts://atl-dc-01")
Concatenating Strings Concatenation is the process of combining two or more strings into a single string. (You can also combine strings with numeric or date values.) Concatenation is often used to provide more readable or more meaningful output. For example, the script in Listing 2.4 returns the value 10340. This is very useful information, provided you know that the script is designed to return the number of megabytes of free disk space on drive C. But if you do not know what the script is designed to do, that output will be meaningless. Among other things, concatenation helps you provide context for your script output. For example, rather than displaying the value 10340, you might want to display a message similar to "There are 10340 megabytes of free disk space." To do this, you must combine the following three items:
As shown in lines 6 and 7 of Listing 2.7, you concatenate items in VBScript by using the ampersand (&). Listing 2.7 Concatenating Strings
1 Const CONVERSION_FACTOR = 1048576
Alternatively, you might have assigned the value "There are " to a variable named MessageStart and the value "megabytes of free disk space." to a variable named MessageEnd. You could then have concatenated the three variables like this:
Wscript.Echo MessageStart & Int(FreeMegabytes) & MessageEnd If you look closely at lines 6 and 7, you will notice that blank spaces were hard-coded into the string values "There are " and " megabytes of free disk space." This is required because the ampersand does not insert any spaces between the items being concatenated. For example, suppose you leave out the blank spaces, like this:
Wscript.Echo "There are " & Int(FreeMegaBytes) & " megabytes of free disk space." In this case, the resulting message box will run the three values together, as shown in Figure 2.6. Figure 2.6 Incorrectly Concatenating String Values For simple forms of concatenation, you can work around this problem by using a comma rather than an ampersand when combining the values:
Wscript.Echo "There are ", Int(FreeMegaBytes), " megabytes of free disk space." When the items are separated by a comma, a blank space is automatically inserted between the items. As a result, the message box is properly formatted, as shown in Figure 2.7. Figure 2.7 Correctly Concatenating String Values
CollectionsUp to this point in the chapter, the scripts have been designed to retrieve the amount of free space on drive C for a specified computer. Determining free space on a single drive is a common administrative task, particularly when you are working with user workstations that are likely to have only one hard drive. Because the intention was to retrieve free disk space for only drive C, the DeviceID (a property of the Win32_LogicalDisk class) was hard-coded into the script.Of course, other computers, including most servers, are likely to have multiple drives. For these computers, determining the free space on drive C tells only part of the story; as a system administrator, you need to know the amount of free space on drive D, drive E, and any other drives installed on the computer. However, this creates a problem: How do you know which drives are installed on a given computer? In theory, you could check for free space on drives C through Z. But if a computer does not have, say, a drive E, the script will fail. Although you can include code designed to handle these errors and prevent the script from failing, the resulting script will be extremely long, making it difficult to read and maintain. Such a script will also be extremely inefficient; even if a computer has only a single drive, the script will nonetheless attempt to retrieve the free space on the nonexistent drives D through Z. Fortunately, Automation objects often return information in the form of collections. Like stamp collections or coin collections, Automation collections are simply a group of related objects. For example, the script in Listing 2.8 uses the WMI method InstancesOf (line 4) to return not just a specific drive but a collection consisting of all the logical disks installed on the computer. If the computer has four drives (C, D, E, and F), the collection will have four items, one for each drive. Listing 2.8 Using Collections
1 Const CONVERSION_FACTOR = 1048576 Having information returned as a collection means you do not have to guess how many drives are installed on a computer. Instead, you simply ask for the collection (all the instances of disk drives installed on the computer). After the collection has been returned, you can use a For Each loop (also known as an iteration loop) to access each individual item in the collection. For Each The For Each statement provides a simple way to iterate through all the items in a collection. Unlike the For Next statement (discussed later in this chapter), For Each does not require you to know how many items are in the collection. Instead, it simply begins with the first item in the collection and continues until it has iterated through every item. A typical For Each loop looks like this:
For Each objLogicalDisk In colLogicalDisk The individual items that make up this loop are described in Table 2.2. Table 2.2 Components of the For Each Statement
When working with collections, you typically iterate through the entire collection rather than refer to a single item within the collection. For example, suppose your collection consists of disk drives C, D, E, F, and G, and you want to echo only the available drive space on drive G. To do this, you will have to set up a For Each loop and begin iterating through the set of disk drives. For each drive in the collection, you can check the drive letter, and echo the available space only for drive G.
Collections with Zero Items It is possible for a collection to contain zero items. For example, consider this script sample, which returns the set of all tape drives installed on a computer:
Set objWMIService = GetObject("winmgmts:")If this script is run on a computer that does not have a tape drive, it will appear that nothing happened. In truth, the script will run as expected. However, because the computer does not have a tape drive, the resulting collection of all tape drives installed on the computer will have zero items in it. When run on a computer without a tape drive, the script will:
However, because no items are in the collection, the For Each loop and any commands included within that loop are not actually run. Instead, the script skips the For Each loop and picks up with the first line following the Next statement. In this sample script, however, no lines of code follow the Next statement, meaning that the script simply stops.
There is no obvious way to tell whether the script actually ran. One way to improve this script is to use the Count property to determine how many items are in the collection. For example, this script sample uses the Count property to echo the number of tape drives installed on a computer:
Set objWMIService = GetObject("winmgmts:")Your script can use the Count property to determine the number of items in the collection, and then do one of two things:
This script might look like the following (the use of the If-Then-Else statement is explained later in this chapter):
Set objWMIService = GetObject("winmgmts:")
LoopingScripts that monitor or measure system resources typically need to collect data at periodic intervals. For example, it is unlikely that you would measure free disk space moments after installing a new hard disk and then never check again to be sure there was still space available on the disk. Instead, you are likely to check free disk space at regular intervals, perhaps once a week, once a day, or even once an hour, depending on the computer being monitored.If there is a relatively long period of time between data collections, you might want to run the script as a scheduled task. This way, you can schedule the script to run every morning at 2:00 A.M., and you never need to give it a second thought. However, using scheduled tasks is not always an option. For example, suppose you want to measure processor use on a computer every 10 seconds until you have collected 500 samples. Although you can create 500 scheduled tasks, one right after another, this is far more trouble than it is worth. A better approach is to run a single script that collects all 500 samples for you. For Next One way to get a single script to run the same set of commands over and over is to enclose those commands within a For Next loop, which allows you to run lines of code a specified number of times. For example, the script shown in Listing 2.9 checks free disk space on a computer every hour for 12 hours. To do this, a For statement is used on line 5 to indicate that the enclosed code block should be run 12 times. Lines 6-10 include the code required to determine the amount of free space for each disk drive on the computer, and line 11 pauses the script for one hour (using a constant that pauses the script for 3,600,000 milliseconds). Line 12 is simply the Next statement, which marks the end of the loop. When the script runs, a connection is made to the remote computer atl-dc-01. The script retrieves the free disk space information and then pauses for one hour. After that hour, the script returns to the first statement of the For Next loop and retrieves free disk space information for a second time. This continues until the disk space information has been retrieved 12 times. After that, the script runs the line of code following the Next statement. Because no lines of code follow that statement, the script completes. Listing 2.9 Running Commands Multiple Times
1 Const CONVERSION_FACTOR = 1048576 The For Next statement allows you to run a block of code a specific number of times. This should not be confused with a For Each statement. For Each is used to iterate through the individual items within a collection. For Next is used to run a particular set of statements a specified number of times. To use a For Next statement, you must determine both a starting point and an ending point. Because For Next statements are typically designed to run a set of statements X number of times, you will generally start with 1 and end with X. Therefore, to do the same thing 10 times, you start with 1 and end with 10.
The For Next statement requires you to use a loop variable (also known as a counter) that keeps a running tally of how many times the code has run. For example, the variable i is used as the counter in the following code sample. The counter starts at 1 and runs the lines of code contained within the For Next statement block. After all the statements have run, the counter is automatically incremented by 1, meaning i is now equal to 2. The script loops back to the beginning of the For Next statement and checks to see whether the value 2 is still within the valid execution range. Because it is, the code within the For Next statement block runs a second time.
For i = 1 to 5 What happens when i is equal to 6? The script will loop back to the beginning of the For Next statement, and check to see if 6 is part of the valid execution range. Because it is not, the For Next statement will immediately stop, and running of the script will continue with the first line following the Next statement. In this case, that is the line that echoes the message "For Next loop complete." The script output looks like this:
1
Making DecisionsOne of the primary reasons for using scripts as a system administration tool is that scripts reduce the need for hands-on human intervention. The scripts introduced thus far in this chapter go a long way towards this goal; the version shown in Listing 2.8, for example, can connect to any computer (even a remote one), bind to the WMI service, and determine the amount of free disk space on each of the hard drives installed on that computer. By running this script on a regular basis, administrators can receive advance notification if any drive begins to run low on disk space.However, it is still up to a system administrator to analyze the script output and determine whether a disk is running low on disk space. The script can be improved by examining the amount of free space and issuing a notification only if the free space has dropped below a specified level. With this approach, administrators are notified only if a disk is running out of space. No notification means that all the disks are in compliance and no action is required. VBScript provides a number of programming constructs that allow scripts to "make decisions." This means a script can analyze a particular piece of data and then take a specified course of action based on the value of that data. The simplest form of decision-making code is the If Then statement, which examines the value of a particular piece of data and compares it against a predetermined value (for example, if the amount of free disk space is less than 100 megabytes). If the statement is True (for example, if only 99 megabytes of disk space are available), the script carries out some action. If the statement is not true, no action is taken. This simple type of decision-making is shown in Listing 2.10. In line 8, the script checks to see whether the amount of free space is less than 100 megabytes (by comparing the value with the constant WARNING_THRESHOLD). If this conditional statement is True (for example, if a drive has only 99 megabytes of free space), the statement immediately following the If-Then statement runs. In this script, that statement appears on line 9, which echoes the message that the drive is low on disk space. If the conditional statement is False (for example, if the drive has 101 megabytes of free disk space), line 9 is not run. Instead, processing passes to line 10, which marks the end of the If Then code block, and the script continues with line 11. Listing 2.10 Making Decisions
1 Const CONVERSION_FACTOR = 1048576 Taking Multiple Actions by Using If Then Else The script shown in Listing 2.10 displays a warning message if a disk drive is low on disk space. If a disk drive has adequate free space, however, no message of any kind is displayed. For a simple monitoring script this is probably acceptable. On the other hand, a user running this script would have no way of knowing whether the lack of output was because all the drives had adequate disk space, or whether the lack of output was because the script failed to run for some reason. In other words, sometimes you want your script to evaluate a condition and then take a different course of action based on that evaluation. For example, you might want to echo a warning message if a drive is low on disk space and echo a "No problem" message if a drive has adequate disk space. This kind of approach can be implemented by using an If Then Else statement. If-Then-Else statements work exactly as the name implies: If a condition is True (or False), Then take this course of action, Else take this course of action. If disk space is low, echo a warning message; otherwise, echo a "No problem" message. An example of this is shown in Listing 2.11. In line 8, the amount of free disk space on a drive is compared against a warning threshold. If the conditional statement is True (that is, if the amount of free disk space is less than the warning threshold), then line 9 runs. But what if the conditional statement is False? To handle this possibility, an Else statement is included on line 10. If the conditional statement is False, and the drive has adequate space, then line 11, the line immediately following the Else statement, runs instead. Listing 2.11 Using an If-Then-Else Statement
1 Const CONVERSION_FACTOR = 1048576 It is possible to construct more elaborate scenarios, scenarios that can take more than just two possible courses of action. Two different ways to construct these scenarios are discussed later in this chapter.
ArraysCollections are an excellent way to package information because they allow you to work with any number of items, even if you do not know the details of any of those items. For example, the script introduced in Listing 2.8 allows you to retrieve the amount of free disk space for all the drives installed on a computer, even if you have no idea how many drives are installed on that computer. To carry out an action on each item in the collection, you simply use a For Each loop to iterate through the collection item by item.Automation objects can create collections for you. However, you might have other information (information not returned from an Automation object) that might be easier to manipulate if you can iterate through the set of items one by one. Suppose you want to check the available disk space on three computers rather than just one. You can write the code to check the first computer, copy and paste the code, and then modify the pasted code to check free disk space on the second computer. You can repeat this process again to check for free disk space on the third computer. Although this approach works, it can quickly become tedious, particularly if you need to check 100 computers. In addition, suppose you need to make a change in the code, perhaps to return not only the free space on the drive but also the total size of the drive. To make that change, you need to change all 100 instances of the code, a process that not only takes a long time to complete but greatly increases the likelihood that you will make an error somewhere along the way. A better approach is to use a For Each loop to iterate through a collection of computers, checking for free disk space on each one. This can be done by placing the computer names in an array, a data structure that can be used in much the same way as a collection. The script in Listing 2.12 places the names of three computers (atl-dc-01, atl-dc-02, and atl-dc-03) in an array and then uses a For Each loop to connect to and retrieve free disk space information from each computer. In line 3, the script creates an array named Computers by using the Array function and specifying the three computer names as the function parameters. (The names are enclosed in quotation marks because they are strings.) In line 4, a For Each loop us used to iterate through all the items in the Computers array. Listing 2.12 Using an Array
1 Const CONVERSION_FACTOR = 1048576 Although arrays are similar to collections, there is one primary difference. As a script writer, you have little control over collections. If you query WMI for a list of disk drives, you will get those disk drives back in whichever order WMI chooses. Furthermore, you will not be able to readily access individual drives without iterating through the entire collection. By contrast, you can control the order of information in an array because you typically populate the array yourself. Furthermore, you have the ability to access individual items in an array without having to iterate through the entire set. This is because each item in an array is assigned an index number. In VBScript, the first item in an array is assigned index number 0, with subsequent items assigned index numbers 1, 2, 3, and so forth. The array created in Listing 2.12 therefore contains the items and index numbers shown in Table 2.3. Notice that the highest index number will always be 1 less than the number of items in the array. Table 2.3 Index Numbers in an Array
You can use these index numbers to access individual items within the array. For example, this line of code will echo atl-dc-02, the value of item 1 (based on index number) in the array:
Wscript.Echo Computers(1) To echo the value of a different item in the array, simply replace the value 1 with the appropriate index number. Additional methods for creating arrays and for accessing the individual items within those arrays are discussed later in this chapter.
InputThe script in Listing 2.12 is designed for an organization in which the computing infrastructure is not expected to change. Needless to say, a static infrastructure such as this is the exception rather than the rule; most organizations have a more dynamic environment. Although there might be only three servers (atl-dc-01, atl-dc-02, atl-dc-03) that need monitoring today, there is no guarantee that only those three servers will need monitoring tomorrow.Because of that, you might not want to hard-code computer names into a script. Hard-coding items such as computers names can lead to a pair of related problems:
There are a number of ways to enter information, such as server names, into a script. (For more information about these methods, see "Creating Enterprise Scripts" in this book.) Perhaps the easiest way is to have the user specify this information as command-line arguments each time the script is run. An argument (also known as a parameter) is information supplied along with the command that actually runs the script. For example, suppose you typically start a script by typing the following at the command line:
cscript FreeDiskSpace.vbs An argument is any information added to the end of that command. For example, this command has three arguments, one for each computer name:
cscript FreeDiskSpace.vbs atl-dc-01 atl-dc-02 atl-dc-03 In addition to supplying arguments, you must include code in the script that makes use of those arguments. This type of coding is covered in detail in the "WSH Primer" chapter of this book. A simple example is also shown in Listing 2.13. In line 9 of this script, a For Each loop is established to iterate through the set of arguments supplied when the script was started. In this script, each argument is successively assigned to the variable Computer and then used to connect to the WMI service on that computer (line 10). The first time the For Each loop runs, Computer will be assigned the value atl-dc-01. On subsequent iterations, Computer will be assigned the value atl-dc-02, and then atl-dc-03. Listing 2.13 Getting User Input
1 Const CONVERSION_FACTOR = 1048576 One benefit of using arguments is that they are automatically placed in a collection (Wscript.Arguments). This makes it easy to iterate through the arguments supplied to a script: You simply set up a For Each loop and iterate through each argument in the collection, exactly as you would iterate through the individual disk drives in a collection of disk drives. Because arguments are placed in a collection, it is easy to verify how many arguments, if any, were supplied when starting a script. In line 4 of the script, Wscript.Arguments.Count is used to determine how many arguments were supplied (which will equal the number of items in the arguments collection). If Count equals 0, meaning no arguments were supplied, a set of usage instructions is displayed, and the script terminates (using the command WScript.Quit).
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||