Hey, Scripting Guy! How Can I Loop Through Collections with Windows PowerShell?

ScriptingGuy1

Hey, Scripting Guy! Question

Hey, Scripting Guy! Your article yesterday was fairly interesting. The problem is that every time that you looped through something, you needed to basically know how many times that you would loop because you needed to be able to count it. Also your analogy about walking on the beach was somewhat dumb. This is because to really follow alongside what you were doing in the script; you would really have to know how many times that you planned to walk on the beach during your week of vacation in Aruba. You also did not allow for what you would do if it rained. You might not have enough sense to come in out of the rain, but I bet the Scripting Wife and scripting friends do. How about giving us a better solution for looping through collections?

– DS

SpacerHey, Scripting Guy! Answer

Hi DS,

Ed here. Your e-mail message reminds me of when I was scuba diving in Maui, Hawaii, in the United States several years ago. It was still dark when I headed to the beach with my dive gear. My dive buddy met me at the beach with the diver propulsion vehicles (DPV). After suiting up and assembling the DPVs, we waded into the surf and headed out to the St. Anthony, a 65-feet former Louisiana Shrimp boat that was sunk as an artificial reef in 70 feet of water off Keawakapu Beach. In addition to the fun of a wreck dive, the St. Anthony is known among locals as a hangout for honu (big green sea turtles). As soon as we ditched the DPVs, we floated over to the wreck. We were in luck because there were several honu hanging out on the deck of the old ship. You can make out a few of them in this picture:

Image of honu hanging out on the St. Anthony

 

Because I did not know how many honu there were on the wreck, I could not use the For statement. Instead, I needed to use the ForEach statement. It goes as follows:

Foreach (honu in St. Anthony)
{
 Take picture without approaching too close or harassing the turtle
}

This week we continue our look at scripting with Windows PowerShell. Windows PowerShell is installed by default on Windows Server 2008 R2 and Windows 7. It is an optional installation on Windows Server 2008, and a download for Windows Vista, Windows XP, and Windows Server 2003. The Windows PowerShell Scripting Hub is a good place to start using Windows PowerShell. An excellent book for learning Windows PowerShell is the Microsoft Press book, Microsoft Windows PowerShell Step by Step. This book has many hands-on labs and uses real-world examples to show the use of Windows PowerShell.

DS, as you may have guessed by now, there is another language statement that you can use in Windows PowerShell to work your way through arrays and collections. It is called the ForEach statement, it resembles the For…Each…Next construction from VBScript. In the DemoForEachNext.vbs script you create an array of five numbers. They number 1 through 5. We then use the For…Each…Next statement to walk our way through the array that is contained in the variable ary. The variable i is used iterate through the elements of the array. The For Each block is entered as long as there is at least one item in the collection or array. When the loop is entered, all statements inside the loop are executed for the first element. In the DemoForEachNext.vbs script, this means that the following command is executed for each element in the array:

Wscript.Echo i

As long as there are more elements in the collection or array, the statements inside the loop continue to execute for each element. When there are no more elements in the collection or array, the loop is exited and execution continues with statement following the Next statement. This is seen in DemoForEachNext.vbs:

DemoForEachNext.vbs

ary = Array(1,2,3,4,5)
For Each i In ary
  WScript.Echo i
Next
Wscript.echo “All done”

Why, you may ask, spend so much time talking about the DemoForEachNext.vbs script? This is because the script works exactly like the DemoForEach.ps1 script. In the DemoForEach.ps1 PowerShell script, you first create an array that contains the numbers 1 through 5 and store that array in the $ary variable. This is seen here:

$ary = 1..5

Then you use the ForEach statement to walk through the array contained in the $ary variable. Use the $i variable to keep track of your progress through the array. Inside the script block (the curly brackets) you display the value of each variable. The DemoForEach.ps1 script is seen here:

DemoForEach.ps1

$ary = 1..5
Foreach ($i in $ary)
{
 $i
}

The cool thing about Windows PowerShell is that you can also use the ForEach statement from inside the Windows PowerShell console:

PS C:\> $ary = 1..5
PS C:\> foreach($i in $ary) { $i }
1
2
3
4
5

The ability to use the ForEach statement from inside the Windows PowerShell console can give you excellent flexibility when you are working interactively. However, much of the work done at the Windows PowerShell console consists of using pipelining. When you are working with the pipeline, you can use the ForEach-Object cmdlet. This cmdlet behaves in a similar manner to the ForEach statement but is designed to handle pipelined input. The difference is that you do not have to use an intermediate variable to hold the contents of the array. You can create the array and send it across the pipeline. The other difference is that you do not have to create a variable for the enumerator. You use the $_ automatic variable (which represents the current item on the pipeline) instead. This is seen here:

PS C:\> 1..5 | ForEach-Object { $_ }
1
2
3
4
5

Suppose that you do not want to work with all the numbers in the array. This is the same thing as if one of the honu on the St. Anthony decides to head to the surface for a breath of fresh air. I could stay at the boat and continue with the next turtle or I could make a break from the ship and follow my friend. This is illustrated here:

Foreach (honu in St. Anthony)
{
 If (honu leaves) { Make a break from ship }
 Take picture without approaching too close or harassing the turtle
}

This syntax is really illustrated in this image, where the honu makes a break from the ship:

Image of a honu making a break from the ship

 

In VBScript terms, leaving a For…Each…Loop early is called an Exit For statement. You have to use an If statement to perform the evaluation of the condition. When the condition is met, you call Exit For. In the DemoExitFor.vbs script you use an inline If statement to make this determination. The inline syntax is more efficient for these kinds of things than spreading the statement across three different lines. The key thing to remember about the inline If statement is it does not conclude with the final End If statement. The DemoExitFor.vbs script is seen here:

DemoExitFor.vbs

ary = Array(1,2,3,4,5)
For Each i In ary
 If i = 3 Then Exit For
 WScript.Echo i
Next
WScript.Echo “Statement following Next”

In Windows PowerShell terms, you use the break statement to leave the loop early. Inside the script block you use an If statement to evaluate the value of the $i variable. If it is equal to 3, you call the break statement and leave the loop. This line of code is seen here:

if($i -eq 3) { break }

The complete DemoBreakFor.ps1 script is seen here:

DemoBreakFor.ps1

$ary = 1..5
ForEach($i in $ary)
{
 if($i -eq 3) { break }
 $i
}
“Statement following foreach loop”

When the DemoBreakFor.ps1 script runs, it displays the numbers 1 and 2. Then it leaves the ForEach loop and runs the line of code following the ForEach loop:

1
2
Statement following foreach loop

If you did not want to run the line of code after the loop statement, you would use the Exit statement instead of the Break statement. This is shown in the DemoExitFor.ps1 script:

DemoExitFor.ps1

$ary = 1..5
ForEach($i in $ary)
{
 if($i -eq 3) { exit }
 $i
}
“Statement following foreach loop”

When the DemoExitFor.ps1 script runs, the line of code following the For loop never executes. This is because the Exit statement ends the script. The results of running the DemoExit script are shown here:

1
2

You could achieve the same thing in VBScript by using the Wscript.Quit statement instead of Exit For. As with the DemoExitFor.ps1 script, the DemoQuitFor.vbs script never comes to the line of code following the For…Each…Loop. This is seen here:

DemoQuitFor.vbs

ary = Array(1,2,3,4,5)
For Each i In ary
 If i = 3 Then WScript.Quit
 WScript.Echo i
Next
WScript.Echo “Statement following Next”

DS, as you can see, you do not have to know how many items are contained within your collection in order to walk through them. In addition, even though you did not ask, we have also looked at exiting a ForEach statement when a condition is met. We hope that you have enjoyed this discussion. Join us tomorrow as we examine decision making. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

0 comments

Discussion is closed.

Feedback usabilla icon