The Foreach Statement

 

SHORT DESCRIPTION

A language command for traversing all items in a collection of items

 

LONG DESCRIPTION

The foreach statement (also known as a foreach loop) is a language construct for stepping through (iterating) a series of values in a collection of items.

 

The simplest and most typical type of collection to traverse is an array. Within a foreach loop it's common to run one or more commands against each item in an array.

 

The following shows the foreach syntax:

 

foreach ($<item> in $<collection>){<command_block>}

 

THE FOREACH STATMEMENT OUTSIDE OF A COMMAND PIPELINE

The part of the foreach statement enclosed in parenthesis ($<item> in $<collection>) denotes a variable and a collection to iterate. The Windows PowerShell creates the variable ($<item>) automatically when the foreach loop runs. Prior to each iteration through the loop, the variable is set to a value in the collection. The block following a foreach statement {<command_block>} contains a set of commands to execute against each item in a collection.

 

For example, the foreach loop in the following example displays the values in an array named $letterArray:

 

$letterArray ="a","b","c","d"

foreach ($letter in $letterArray)

{

Write-Host $letter

}

 

In this example, the $letterArray is created and initialized with the string values "a", "b", "c" and "d". Then, the first time the foreach statement runs, it sets the $letter variable equal to the first item in $letterArray ("a"), and then uses the Write-Host Cmdlet to display the letter a. The next time through the loop, $letter is set to "b", and so on. After the foreach loop displays the letter d PowerShell exits the loop.

 

Note that the entire foreach statement must appear on a single line to run it as a command at the PowerShell command prompt. This is not the case if you place the command in a .ps1 script file instead.

 

Foreach statements can also be used in conjunction with Cmdlets that return a collection of items. In the following example, the foreach statement steps through the list of items returned by the Get-ChildItem Cmdlet:

 

foreach ($file in Get-ChildItem)

{

Write-Host $file

}

 

You can refine the example by using an if statement to limit the results returned. In the following example, the foreach statement performs the same looping operation as the previous example, but it adds an if statement to limit the results to files that are greater than 100 KB in size:

 

foreach ($file in Get-ChildItem)

{

if ($file.length -gt 100k)

{

Write-Host $file

}

}

 

Notice in this example that the foreach loop uses a property of the $file variable to perform a comparison operation ($file.length -gt 100k). The $file variable contains all of the properties contained in the object returned by the Get-ChildItem Cmdlet. Therefore, you can return more than just a file name. In the next example, PowerShell returns the length and the last access time inside of the command block:

 

foreach ($file in Get-ChildItem)

{

if ($file.length -gt 100k)

{

Write-Host $file

Write-Host $file.length

Write-Host $file.lastaccesstime

}

}

 

Notice also in this example that you are not limited to running a single command inside of a command block.

 

You can also use a variable outside of a foreach loop that is incremented inside of the loop. In the following example, the $i variable is set to 0 outside of the loop and is incremented inside of the loop for each file found that is larger than 100 KB. When the loop exits, an if statement evaluates the value of $i to display a count of all files over 100 KB or it displays a message stating that no files over 100 KB were found.

 

$i = 0

foreach ($file in Get-ChildItem)

{

if ($file.length -gt 100k)

{

Write-Host $file "file size:" ($file.length /

1024).ToString("F0") KB

$i = $i + 1

}

}

 

if ($i -ne 0)

{

Write-Host

Write-Host $i " file(s) over 100 KB in the current

directory."}

else

{

Write-Host "No files greater than 100 KB in the current

directory."

}

 

The previous example also demonstrates how to format the file length results ($file.length / 1024).ToString("F0"). The value is divided by 1024 to show the results in KB rather than bytes and the resulting value is then formatted using the fixed-point format specifier to remove any decimal values from the result. The 0 tells the format specifier that it should not show any decimal places.

 

THE FOREACH STATEMENT INSIDE OF A COMMAND PIPELINE

When foreach appears in a command pipeline, PowerShell uses the foreach alias that calls the Foreach-Object. When you use the foreach alias in a command pipeline, you do not include the ($<item> in $<collection>) syntax as you do with the foreach statement. This is because the prior command in the pipeline provides this information. The syntax of foreach when used in a command pipeline is:

 

<command> | foreach {<command_block>}

 

For example, the foreach loop in the following command pipeline displays any processes whose working-set (memory usage) is greater than 20 MB. PowerShell pipes the output from the Get-Process command to the foreach alias. Inside the foreach alias command block, $_.WS contains the value of the WS (working-set) property passed to it by the Get-Process Cmdlet (the $_ portion of the declaration is a WSH automatic variable and the WS portion is a property). The if statement uses a conditional statement to determine whether the working-set is greater than 20 MB (20000000 bytes). If so, then the name of the process (stored in $_.name) and the working-set size in megabytes is displayed. If no process working-set is over 20 MB, nothing is displayed.

 

Write-Host "Processes with working-sets greater than 20 MB"

Get-Process | foreach {

if ($_.WS -gt 20m)

{

Write-Host $_.name ": "

($_.WS/1m).ToString("F0") MB -Separator ""

}

}

 

The foreach alias also supports the notion of a beginning command block, a middle command block and an end command block. The beginning and end command blocks run once and the middle command block runs every time the foreach loop steps through a collection or array.

 

The syntax of the foreach alias when used in a command pipeline with a beginning, middle, and ending set of command blocks is:

 

<command> | foreach {<beginning command_block>}{<middle command_block>}{<ending command_block>}

 

The following example demonstrates the use of the beginning, middle, and end command blocks.

 

Get-ChildItem | foreach {

$fileCount = $directoryCount = 0}{

if ($_.PsIsContainer) {$directoryCount++} else {$fileCount++}}{

"$directoryCount directories and $fileCount files"}

 

·         The beginning block: {$fileCount = $directoryCount = 0} creates and initializes two variables to 0.

·         The middle block: {if ($_.PsIsContainer) {$directoryCount++} else {$fileCount++}} Evaluates whether each item returned by Get-ChildItem is a directory or a file. If it's a directory, the $directoryCount variable is incremented by 1, if it's not a directory, the $fileCount variable is incremented by 1.

·         The ending block: {"$directoryCount directories and $fileCount files"} Runs after the middle block completes its looping operation and then it outputs the results of the operation.

 

By using the beginning, middle, and ending command block structure and the command pipe, you can rewrite the earlier example demonstrating how to find any files that are greater than 100 KB in size, as shown.

 

Get-ChildItem | foreach{

$i = 0}{

if ($_.length -gt 100k)

{

Write-Host $_.name "file size:" ($_.length /

1024).ToString("F0") KB

$i++

}

}{

if ($i -ne 0)

{

Write-Host

Write-Host "$i file(s) over 100 KB in the current

directory."

}

else

{

Write-Host "No files greater than 100 KB in the current

directory."}

}

 

SEE ALSO

For information about if statements, enter the following command at the

Windows PowerShell command prompt:

 

help about_if

 

For information about the Foreach-Object, enter the following command

at the Windows PowerShell command prompt:

 

help Foreach-Object

 

For information about automatic variables, enter the following command

at the Windows PowerShell command prompt:

 

help about_automatic_variables