Thrown for a Loop, Part 2
Welcome to Sesame Script, the column for beginning script writers. The goal of this column is to teach the very basics of Windows scripting for system administration automation. We’ll provide you with the information you’ll need to begin reading and understanding scripts and to start modifying those scripts to suit your own needs. If there’s anything in particular about scripting you’re finding confusing, let us know; you’re probably not alone.
Check the Sesame Script Archives to see past articles.
Download all the Sesame Script columns (from the very first column through the September, 2007 edition) in one easy-to-read, fully-searchable .chm file.
On This Page
Did you know that 75% of the raisins eaten in the U.S. are eaten at breakfast? This isn’t really surprising if you think about it: Kellogg’s Raisin Bran cereal, raisin muffins, cinnamon rolls with raisins, oatmeal with raisins…yep, pretty popular breakfast food. As usual, though, the Scripting Guy who writes this column tends to go against the norm - 75% or the raisins this Scripting Guys eats are eaten later in the day, and they’re covered in chocolate. All the breakfast things are fine, but the chocolate-covered snack beats them all.
Just an interesting fact that has absolutely nothing to do with anything.
Slightly over two years ago we wrote a Sesame Script on using the For Each loop and the For Next loop to loop through arrays. We also mentioned that there are other ways of looping and that we’d talk about that someday. Well, someday is finally here. We’re pretty sure two years of suspense is enough, meaning that we can finally reveal two more ways of looping through a script: the While Wend loop and the Do loop.
While … Wend
Let’s just jump right in and look at a While statement:
a = 5 While a < 10 Wscript.Echo a a = a + 1 Wend
In this script we’ve set the variable a to the value 5. We then start our While statement. The While statement checks for a true or false condition; in this case we’re checking to see whether it’s true that the value of a is less than 10. If the condition is true - that is, if a is less than 10 - we enter the While loop and run the lines of code between the While statement and the Wend statement. The first time through the loop a is equal to 5, which means that the condition a < 10 is true. Therefore, we enter the loop and echo the value of a.
The next thing we do is add 1 to the value of a. This is very important, which you’ll realize in just a minute (if you haven’t already). After we add 1 to a we reach the Wend statement. (That’s pretty clever syntax isn’t it? The If statement has an End If, the Sub statement has an End Sub…but who wants to type End While? Wend is shorter and sounds kind of cool. Well, we said “kind of.”) When we reach the Wend statement we go back to the top of the loop; that is, back to the While statement. At this point we check the condition again - is a less than 10? If it is (which, the second time through the script, it still will be) we once again enter the loop, echo the value of a, then add 1 more to a. This continues until a is no longer less than 10; when that happens the While condition evaluates to false and we’ll end up skipping the loop and going to the next statement in the script following Wend. Since this particular script doesn’t have a next statement, the script simply ends.
If you’re still wondering what was so important about adding 1 to the value of a, give this script a try and see what happens (we strongly recommend you run this script under cscript rather than wscript):
a = 5 While a < 10 Wscript.Echo a Wend
Welcome to the “endless loop.” If you ran this you have a bunch of 5s endlessly displaying in your command window. To stop them either press Ctrl+C or close the command window. (If you made the mistake of running this under Wscript, meaning you’re getting a message box with a 5 in it over and over again, start Task Manager and end the wscript.exe process.) An endless loop is a loop where the condition statement stays true forever. In this case we set a to 5, we checked to see if a was less than 10, and because it was we entered the loop. Now every time we loop back around and check to see if a is less than 10, this will always be true. Why? Because we never change the value of a. We’ll be in this loop forever.
Sure, that was kind of a mean trick to tell you to run that script, but believe us, this won’t be the last time you do something like this. The only difference is that from now on it will always be an accident. And at least now you’ll usually recognize when you’re in an endless loop and you’ll know how to fix it: look for the reason your condition statement is never becoming false. This is often a little trickier than what we’ve shown you here, but at least you know where to start. If you have trouble, take a break, eat some chocolate-covered raisins, and try again.
The While Wend loop was pretty simple, so now let’s take a look at the Do loop. There are actually four different ways to call the Do loop:
Do While <condition> ... Loop
Do Until <condition> ... Loop
Do ... Loop While <condition>
Do ... Loop Until <condition>
Actually, there is a fifth option:
Do ... Loop
We’ll get to that last one in a minute. First we need to eat a few more chocolate-covered raisins and explain the first four loops. We’ll then look at the last one, well, last.
Do While … Loop
The Do While loop is identical in functionality to the While Wend loop that we just looked at. Here’s an example:
We once again start by setting the variable a to 5. This time, instead of simply saying While, we use the Do While statement. However, the next part is the same: we check to see if it’s true that a is less than 10. If it is (and the first time through the loop it is) we enter the loop, echo the value of a, then add 1 to a (and we know now not to forget that). At this point we’ve reached the Loop statement which, like the Wend statement, simply marks the end of our loop. We then go back to the top of the loop, check to see if our condition (a < 10) is still true, and continue from there.
So why would you want to use the Do While … Loop syntax rather than simply using While … Wend? They both do the same thing so it doesn’t really matter. The only reason to use the Do statement rather than the While statement is because this is the only type of looping you can do with the While Wend statement. The Do Loop statement has other variations, so it might be easier to use the Do Loop statement all the time just to get used to the syntax.
Speaking of other variations, let’s take a look at the next one.
Do Until … Loop
In a way, this loop is the opposite of the Do While loop. For example, let’s say you have a bag of chocolate-covered raisins. You can continue eating raisins while there are raisins in the bag; on the other hand you can continue eating raisins until there are no raisins in the bag. (Either way, we can’t think of why you’d stop eating chocolate-covered raisins before then.) See what we mean about opposites? We’re really testing for the same thing (all the raisins are gone); we’re just going about it from different directions. In the case of the While statement, the condition is true if there are raisins; in the case of the Until statement, the condition is true when there are no raisins.
All right, let’s look at a script example:
Here we’ve once again set our variable a to 5. When we get to our Do loop we check to see whether the condition, in this case a > 10, is true. Notice that in our Do While example we checked to see if a was less than 10; this time we’re checking to see if it’s greater than 10. Remember, we want to perform the steps in the loop until the condition is true, which means we’ll enter (and stay in) the loop as long as the condition is false. The first time through the loop a is not greater than 10, which means the condition is false; therefore, we enter the loop. We echo the value of a and then add 1 to a; by then we’ve reached the Loop statement and we go back to the top of the loop. We check the condition again; if it’s still false we go into the loop again. And so on until the condition is true. As soon as a is greater than 10 we fall out of the loop and, in this case, the script ends.
Do While vs. Do Until
Why would you want to use a Do While rather than a Do Until, or the other way around? In many cases it’s just a matter of preference, but in some cases it really depends on what you’re trying to accomplish. We can take a look at the out put of our two scripts to get an idea of how this works. Let’s look again at our Do While script:
Here’s our output:
5 6 7 8 9
Now look at our Do Until script:
And here’s that output:
5 6 7 8 9 10
As you can see, we got more output with our second script than we did with our first script. In the case of our Do While script, we continued the loop while a was less than 10, which means that when a reaches a value of 10 we don’t go through the loop again, so we never echo the value 10. In the Do Until script we continued to loop until a was greater than 10. That meant that when a reached a valued of 10 we performed the loop one more time, incremented a to a value of 11, and at that point a was greater than 10 so we stopped looping.
We could easily have made these two scripts produce the same output. One simple thing we could have done was test for a value of 11 in the Do While script to get an output of 5 through 10:
a = 5 Do While a < 11 Wscript.Echo a a = a + 1 Loop
Or a value of 9 in our Do Until script to get output of 5 through 9:
a = 5 Do Until a > 9 Wscript.Echo a a = a + 1 Loop
But maybe 10 has some significance in your script - you want to know that you’re comparing to a value of 10. Or maybe you’re comparing to a constant, like this:
Const b = 10 a = 5 Do Until a > b Wscript.Echo a a = a + 1 Loop
If you really only wanted the values 5 through 9 in the output of this Do Until loop, you’d need to change your condition:
Const b = 10 a = 5 Do Until a >= b Wscript.Echo a a = a + 1 Loop
In this case we’ve checked for a being greater than or equal to (>=) 10.
Note: Yes, in this case we could have simply used equal to (=) and the loop would have ended as soon as we reached 10:
Const b = 10 a = 5 Do Until a = b Wscript.Echo a a = a + 1 Loop
But what happens if we go back later and change our incrementing, adding 2 to a each time through the loop rather than 1?
Const b = 10 a = 5 Do Until a = b Wscript.Echo a a = a + 2 Loop
The first time through the loop a is equal to 5; a is not equal to 10, so we enter the loop. The next time through a is equal to 7, so we loop again. Next a is 9, we loop again. Next, a is 11…now we’re in trouble. The value of a will never be equal to 10, meaning we’re in an endless loop again. If you’re working with numbers, it’s always a good idea to be very careful when using the equals operator in your condition statements.
A Practical Example
It’s likely that at some point you’ll need Do loops that compare values like we just did, or even compare strings. But another case in which Do loops come in handy is when reading from text files:
Const ForReading = 1 Set objFSO = CreateObject("Scripting.FileSystemObject") Set objFile = objFSO.OpenTextFile("C:\Scripts\Servers.txt", ForReading) Do Until objFile.AtEndOfStream strLine = objFile.ReadLine Wscript.Echo strLine Loop objFile.Close
This script is explained fully in the Sesame Script article Scripting Text Files, so we’re only going to focus on the Do Until loop:
Do Until objFile.AtEndOfStream strLine = objFile.ReadLine Wscript.Echo strLine Loop
In this script we’ve opened a text file for reading and saved a reference to that text file in the object objFile. (If you have no idea what we just said and don’t know what objects and references are, see the article Class is in Session.) When we read from a text file using the FileSystemObject we can either read the entire file into memory at once or read it in line-by-line. In this case we’ve chosen to read it in line-by-line (that’s what the ReadLine method does), echoing each line as we go. In order to read a file in one line at a time, we need to loop around, reading each line until we’ve reached the end of the file. We know we’ve reached the end of the file when the AtEndOfStream property is true. And that just happens to be our condition statement:
Do Until objFile.AtEndOfStream
Here we’re stating that we’re going to perform all the lines of code in the loop until the AtEndOfStream property is true.
Note: Some of you might be wondering where the True statement is:
Do Until objFile.AtEndOfStream = True
We don’t actually need to do that. The Do Until statement just needs something that evaluates to True or False. The property AtEndOfStream contains a value of True or False, so simply putting that property in as the condition is the same as saying Do Until True or Do Until False, depending on the value of the property. Just a little shorthand to make life easier.
We could have used a Do While statement, but in that case we’d have to check that we’re not at the end of the file:
Do While Not objFile.AtEndOfStream strLine = objFile.ReadLine Wscript.Echo strLine Loop
This is perfectly acceptable, but usually not recommended. Scripts can start to get confusing if you put a lot of negative statements in. If you can test for a True condition rather than a Not True condition your script will usually be easier to read.
Do … Loop While
Another thing you can do with a Do loop is put the condition at the end of the loop:
a = 5 Do Wscript.Echo a a = a + 1 Loop While a < 10
In this script we’ve set the variable a to 5, then we have a Do statement all by itself. When there’s no condition next to the Do, that means we’re going to go straight into the loop. We echo the value of a, add 1 to a, then we reach the Loop statement. Now we check the condition statement. If a is less than 10, we’ll head back up to the Do statement and start the loop over again; if a is not less that 10 (the condition is false), the loop ends, and in this case so does the script.
Why would we put the condition statement at the end of the loop? Because by doing this we guarantee we’ll run the lines of code in the loop at least once. Let’s look at a slightly different example:
a = 5 Do Wscript.Echo a a = a + 1 Loop While a < 5
This time we’re check to see whether a is less than 5. At the start of our loop a is not less than 5, meaning the condition is false; however, we still echo the value of a. This also allows us to increment our value before checking. This script will also go through the loop only once:
a = 5 Do Wscript.Echo a a = a + 1 Loop While a < 6
We start out with a value of 5, echo that value, but then we add 1 to the value. The variable a now contains a value of 6, so when we check out the condition a is not less than 6 we don’t run through the loop again.
Do … Loop Until
You can also put the condition at the end of the script with the Until statement:
a = 5 Do Wscript.Echo a a = a + 1 Loop Until a > 10
You can probably figure this one out. We set a to 5 then enter the loop, echoing the value of a and then adding 1. We then check to see whether a is greater than 10; if it is we stop looping. If it isn’t we go back to the top of the loop and start again.
One Last Loop
We mentioned a fifth option for the Do loop. Here it is:
a = 5 Do Wscript.Echo a a = a + 1 If a > 10 Then Exit Do End If Loop
Notice that we don’t have a condition at the beginning or the end of our loop. What we’ve done is purposely set up an endless loop. But we know from experience that endless loops are not good. (An endless supply of chocolate-covered raisins, on the other hand….) The only way to get out of a loop like this is to make sure you have an Exit Do statement in your loop:
If a > 10 Then Exit Do End If
Of course, this isn’t a practical example at all; it would make much more sense to do this type of check with a While or Until statement. This is just an example to show you how to get out of one of these loops.
You want a practical example? Okay, here you go:
Do strAnswer = InputBox _ ("Please enter a file name:","File Name") If strAnswer = "" Then Wscript.Echo "You must enter a file name." Else Wscript.Echo strAnswer Exit Do End If Loop
In this script the first thing we do is enter the loop. Inside that loop we use the InputBox function to display an input box to the user. The InputBox function returns the value that was entered in the box, which we’ve stored in the strAnswer variable. If the user clicks OK without entering anything (or the user clicks Cancel) strAnswer will be empty. Well, we’ve decided we’re going to force them to enter a value, so we check to see whether strAnswer is empty. If it is we echo a message then loop around and display the input box again. The input box will continue to be displayed until something is entered and the user clicks OK. When that happens we echo the input, then call Exit Do to get out of the loop.
Could we have tested strAnswer in a Do While or Do Until condition statement? Not if we want to echo a statement explaining why we’re displaying the input box again. True we could do something like this:
Do strAnswer = InputBox _ ("Please enter a file name:","Create File") If strAnswer = "" Then Wscript.Echo "You must enter a file name." Else Wscript.Echo strAnswer End If Loop While strAnswer = ""
In this case it’s just a matter of how you prefer to do things.
The place you’d probably use this statement most frequently is in monitoring scripts. Monitoring scripts GO way beyond the scope of this article, but if you’d like to see an example of using Do Loop in a monitoring script, here’s a script that will check every 30 minutes to see whether a file has been modified:
strComputer = "." Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colFiles = objWMIService.ExecQuery _ ("Select * from CIM_Datafile Where Name = 'C:\\Scripts\\Application.log'") For Each objFile in colFiles strOriginalTimestamp = objFile.LastModified Next Wscript.Echo "Monitoring application log file: " & Now Wscript.Echo Do Wscript.Sleep 1800000 Set colFiles = objWMIService.ExecQuery _ ("Select * from CIM_Datafile Where Name = 'C:\\Scripts\\Application.log'") For Each objFile in colFiles strLatestTimestamp = objFile.LastModified Next If strLatestTimestamp <> strOriginalTimestamp Then strOriginalTimestamp = strLatestTimeStamp Else Wscript.Echo "ALERT: " & Now Wscript.Echo "The application log file has not been modified in the last 30 minutes." Wscript.Echo strOriginalTimestamp = strLatestTimeStamp End If Loop
For an explanation of this script, see this Hey, Scripting Guy! article. The one thing we’ll point out is that because this script is designed to monitor something, not only is there no condition statement on the Do loop, there’s also no Exit Do. This script will continue until you press Ctrl+C or exit the command window (remember to start with cscript).
The Suspense is Over
Finally, after two years of waiting, we’ve completed our discussion of looping in VBScript. It was well worth the wait, wasn’t it? It wasn’t? Well, at least we don’t have to wait that long to get some more chocolate-covered raisins. Maybe we’ll have some for breakfast tomorrow.