Hello Readers, Prateek here…. I'm so excited to announce that this article is first one of its kind "a deep dive" written by friend and guest Author 'Akshi Srivastava'. I'm hoping that you will enjoy reading this long form of content and by the end of this article you will definitely learn a few tips, tricks and concepts.. happy learning! ♥


Table of Contents

  1. FOR Loop
    • For Loop with multiple repeat placeholder
    • Multiplication and other operations in Repeat placeholder of For Loop
    • Nested For Loop
    • Infinite For Loop
  2. WHILE Loop
  3. DO-WHILE Loop
  4. Difference Between While and Do-While Loop
  5. DO-UNTIL Loop
  6. Difference between Do-While and Do-Until
  7. FOREACH Loop
    • How PowerShell ForEach Iteration Works
    • Foreach with Range Operator
    • Foreach with Array of characters
    • Foreach loop on results of a command\cmdlet or an expression
    • Foreach loop with Label
    • The Continue And Break Keywords in Foreach Loop
    • The Special $foreach Variable
  8. Foreach-Object Cmdlet
    • [scriptblock] with pipeline input
    • [scriptblock] with InputObject parameter
    • Difference between Foreach and Foreach-Object
  9. Performance Foreach vs Foreach-Object
  10. Foreach Method
  11. Ways to use Foreach() Method
    • ForEach(scriptblock expression)
    • ForEach(scriptblock expression, object[] arguments)
    • ForEach(type convertToType)
    • ForEach(string propertyName)
    • ForEach(string propertyName, object[] newValue)
    • ForEach(string methodName)
    • ForEach(string methodName, object[] arguments)
  12. Differences between Foreach-Object and Foreach() Method

WHAT IS A LOOP?

A loop is a programming/scripting language construct, that lets you define arbitrary or a predefined sequence of instructions inside the body of the loop, that can iterate (repeat) number of times as long as the defined condition is $true.

In PowerShell, Loops are classified into 7 types –

  1. For Loop
  2. While loop
  3. Do-While loop
  4. Do-Until loop
  5. Foreach-Object cmdlet
  6. Foreach Statement
  7. Foreach() Method

FOR LOOP

For loops, also known as For Statement are typically used to iterate through a set of commands a specified number of times, either to step through an array or object or just to repeat the same block of code as needed.

Syntax:

                      for(<Initialize>; <Condition>; <Repeat>)  {  Command            1            …  …  Command n }                  

Initialize, condition and repeat are separated by a semi-colon ';' and are wrapped inside a parenthesis '()'

Initialize: You initialize a loop counter (variable) with starting value and it will always be run before the loop begins. For example, if you are running the loop to iterate 10 times, you can initialize it with 0 and end up loop counter at value 9 making it total 10 repetitions.

Condition: At the beginning of each iteration of the For loop a condition is tested that results in either a $true or a $false Boolean value.
If the condition is $true the body of the loop is executed, and if it is $false then flow of control exits the loop and goes to the next line after the loop.

Repeat: A set of commands run each time the loop repeat, mostly it is used to increment or decrement the loop counter.

Example 1:

                      # $i=1 initialize the loop counter with 1                        # and $i++ increment the value by 1            For            ($i=1;            $i            -le            10;            $i++) {            # print the multiplication table of 10            # i.e. simply by multiply $i with 10            "10 *              $i              = $(10 *              $i)"            }                  

Result:

For Loop with multiple repeat placeholders

A Repeat placeholder can hold more than one command and each repeat statement is separated by commas. In this case, we have to initialize each loop counter that we will use in repeat placeholder for increment/decrement operation, for example:

Example 2:

                      $j=10            For($i=1;$i            -le            10;$i++,$j--){            "Loop Iteration : i=$i              and j=$j"            }                  

Results:

Multiplication and other operations in Repeat placeholder of For Loop

Instead of using increment/decrement command, we can also use the multiplication operator and other kind of operations:

Example 3:

                      For($i=2;$i            -le            20;$i=$i*2){            $i            }                  

Results:

This functionality is not only limited to mathematical operations on numbers like, addition and multiplication but you can perform any operations like concatenation on a [String] as demonstrated in the following example:

Example 4:

                      For($str=''            ;$str.length            -le            10;$str=$str+'a'){            $str            }                  

Results:

Nested For Loop

A loop within a loop is known as a Nested Loop.

Example 5:

                      For($i=1;$i            -le            5;$i++){            For($j=1;$j            -le            $i;            $j++){            Write-Host            "*"            -NoNewline     }            Write-Host            ""            }                  

Result:

Infinite For Loop

All placeholder can also be empty and if there is no condition to test and control the number of iterations of the loop that will result in an infinite loop. Which will keep executing until you break the flow of control explicitly by pressing CTRL+C.

Example 6:

                      For(  ;  ;  ) {            "Infinite loop"            }                  

Result:

WHILE LOOP

Repeats a command or group of commands while a given condition is $true. The While loop tests the loop condition before the first iteration and the set of commands in the loop body are not executed, even once, if the loop condition doesn't match, therefore it is also called as a Pretest Loop . Since, flow of loop is getting controlled at the beginning of loop because of condition, so we can say it Entry Controlled Loop.

Unlike for loop, the condition can only contain a boolean expression enclosed in parenthesis '()'. In while loop, you have to initialize the loop counter before the while statement and increment or decrement statement is defined at the end of the loop body.

Syntax:

                      # Initializing            While(condition) {     Command-1            ...     Command-n            # Increment/decrement            }                  

Example 7:

                      $i=1            # initialize loop counter            # while loop condition            While            ($i            -le            10)    {            $i            # print the current value            $i++            #increment loop counter            }                  

Result:

You can also call cmdlets and assign values in the loop condition. Below example demonstrates this:

Example 8:

                      while(($userinput            =            Read-Host            -Prompt            "Select a command")            -ne            "Q"){            switch($userinput){         L {"File will be deleted"}         A {"File will be displayed"}         R {"File will be write protected"}         default {"Invalid entry"}     } }                  

Results:

In some situations, you may need to exit a loop early based on something other than the condition of the loop. In this case, the Break keyword can be invoked in order to exit out of the loop. This final example shows the same functionality but uses an infinite loop and the Break keyword to exit out at the appropriate time.

Example 9:

                      $i=1            While            ($true){            $i            $i++            if            ($i            -gt            10) {            Break            } }                  

Result:

DO-WHILE LOOP

Do-While loop is just like a while loop, executes the block of statements while the condition evaluates to Boolean $true, but there is a small difference that is, the body of Do-While is executed first then the condition is tested at the end of the loop compared to a While loop. Therefore, we termed this loop as Post-Test Loop also.

Since the condition is tested at the end a Do-While loop is an "Exit controlled loop", that means even if the Condition evaluates to Boolean $false a Do-While loop iterates at least once, because condition is tested after the body of the loop is executed.

Syntax:

                      Do            {      Command sequence }            While            (<condition>)                  

Let see an example to understand the behavior of do-while loop

Example 10:

                      $i            =            1            Do            {            "Loop Iteration i=$i"            $i++  }            While($i            -le            10)                  

Result:

Let use the calculator example, with the Do-While which actually simplifies the logic and reduces few lines of code.

Example 11:

                      $a            =            3;            $b            =            2            Do            {            "`$a =              $a              `$b =              $b"            $Choice            =            Read-Host            "1. Add`n2. Substract`n3. Multiply`n4. Divide`n5. Exit`nChoose a number [1-5]"            switch($Choice)      {            1            {$a+$b}            2            {$a-$b}            3            {$a*$b}            4            {$a/$b}            5            {Write-Host            "Exiting the Loop"            -ForegroundColor Yellow;exit}          Default {Write-Host            "Wrong choice, Try again"            -ForegroundColor Red}      }  }            While($Choice            -ne            5)                  

Result:

Even if we enter 5 first as an input, it will execute at least once as per its behavior i.e. execute the 5th case and then exit from the loop.

Example 12:

                      $a            =            3;            $b            =            2            Do            {            "`$a =              $a              `$b =              $b"            $Choice            =            Read-Host            "1. Add`n2. Substract`n3. Multiply`n4. Divide`n5. Exit`nChoose a number [1-5]"            switch($Choice)      {            1            {$a+$b}            2            {$a-$b}            3            {$a*$b}            4            {$a/$b}            5            {Write-Host            "Exiting the Loop"            -ForegroundColor Yellow;}          Default {Write-Host            "Wrong choice, Try again"            -ForegroundColor Red}      }  }            While($Choice            -ne            5)                  

Result:

Difference Between While and Do-While Loop

While Loop Do-While Loop
Entry controlled loop Exit controlled loop
Tests the condition before execution of first iteration ( Pre-test Loop ) Test the condition after execution of first iteration( Post-test Loop )
Loop is not executed when condition evaluates to $false Loop is executed for at least once even condition evaluates to $false
Do not use any other keyword except while Use do keyword at starting of loop body and while keyword with condition at the end of loop

DO-UNTIL LOOP

Do-Until loops have a similar syntax to Do-While, both begin with the Do keyword prefacing a script block, followed by the condition keyword (While or Until). And a condition enclosed in parenthesis ( ) to stop further processing, until the condition is evaluated to $true. That means the body of loop would be repeated until the condition is met, this is exactly opposite of Do-While loop where the body of the loop only executes if the condition is $true.

Syntax:

                      Do            {  Command sequence  }            Until            (<condition is true>)                  

Let see an example to understand the behavior of Do-Until loop

Example 13:

                      $i            =            1            Do            {            "Loop Iteration i=$i"            $i++  }            Until($i            -gt            10)                  

Result:

Difference b/w Do-While and Do-Until

Do-While Loop Do-Until Loop
while keyword for the condition Until keyword for the condition
Continue execution while condition is $true Continue execution until the condition is $true

FOREACH LOOP

Foreach statement or "Foreach loop" is PowerShell scripting language construct that is used to iterate through values in a collection of items, like an Array and perform defined operation against each item.

How PowerShell ForEach Iteration Works

To iterate through a collection of items, you take the first item in the collection and perform an action. When you complete the task on the first item, you take the next item and perform the same action on the item. Then you continue the same process until you have processed through all the items in the collection. The syntax of a PowerShell ForEach construct is shown below:

Syntax:

                      ForEach            (Item            In            Collection) {      ScriptBlock or sequence of commands  }                  
  • Item: The variable to hold the current item.

  • Collection: A collection of objects, like ComputerName or Files. Collections will be evaluated and stored in memory before the ScriptBlock is executed.

  • ScriptBlock: A sequence of commands to be executed against each item in the collection.

The construct of a 'ForEach PowerShell' loop is very straight forward. It simply says: ForEach $item in the $collection, perform the task(s) enclosed in the '{}' block.

The entire Foreach statement must appear on a single line to run it as a command at the Windows PowerShell command prompt. The entire Foreach statement does not have to appear on a single line if you place the command in a .ps1 PowerShell script file instead.

The simplest use case is traversing the items or elements of an array, as demonstrated in the following example.

Example 14:

                      # variable holds array of integers            $Collection            =            1,2,3,4,5            ForEach($Item            in            $Collection) {            "Current Item =              $Item"            }                  

Result:

And there can many ways how you define a collection in a ForEach statement as demonstrated in following examples:

Foreach with Range Operator

A collection is an array of items from 1 to 5 defined using a Range operator (..)

Example 15:

                      # use range operator to define collection            ForEach($Item            in            1..5){            "Current Item =              $Item"            }                  

Result:

Foreach with Array of characters

Define the collections as an array of characters

                      ForEach($Item            in            'a','b','c','d','e') {            "Current Item =              $Item"            }                  

Results:

Assign value of multiple variables to a single variable to make it a collection or array.

Example 17:

                      $a            =            5            $b            =            6            $c            =            7            $d            =            $a,$b,$c            Foreach            ($i            in            $d){            $i            +            5            }                  

Result:

Foreach loop on results of a command\cmdlet or an expression

Store the output of PowerShell cmdlet or expression in a variable and pass that variable as a collection in foreach statement.

Example 18:

                      $Service            =            Get-Service            -Name            "a*"            ForEach($s            in            $Service) {            $s.Name }                  

Results:

Run expression and commands directly in the Foreach statement to return a collection of items. In the following example, the Foreach statement steps through the list of items that is returned by the Get-ChildItem cmdlet.

Example 19:

                      # use command directly in foreach loop            ForEach($Item            in            Get-ChildItem            C:\Test\*.txt) {            "Current Item =              $Item"            }                  

Result:

You can save the results generated by ForEach statement, all you have to do is add a variable just before the Foreach statement and all data would be stored in that variable which can be used to perform the further operation based on your requirement.

For example to calculate the total CPU consumption by Processes with name chrome.exe, we can define a $CPU variable front of Foreach statement to capture the result generated by the ForEach body.

Example 20:

                      # store foreach output in a variable            $CPU            =            Foreach($item            in            (Get-Process            chrome*)){            $item.CPU }            $totalCPU            = ($CPU            | measure -Sum).sum            "Total CPU:              $totalCPU"                  

Result:

If you want to do some filtration task on a collection of objects and perform some operation on filtered objects, then you can simply write some conditional statements inside the loop which can filter out the objects for you.

Example 21:

                      $Service            =            Get-Service            ForEach($s            in            $Service) {            # Use if block to do filter operation on current item in loop                        If($s            -like            "A*") {            if($s.Status            -eq            "Stopped") {            write-host            "[$($s.Name)] : $($s.StartType) and Stopped"            }            else            {            Write-Host            "[$($s.Name)] : $($s.StartType) and Running"            }     } }                  

Result:

Foreach loop with Label

In Foreach loop we can also use a Label to control flow of execution of the code, following example demonstrates how the outermost loop is tagged with a label :OUTER just before the foreach keyword, indicating the name of the label (OUTER). When you use the continue or break keywords, you can pass this label name as an argument, to indicate the block to break out of (if no argument is passed, the innermost block is targeted).

Example :

          :OUTER            foreach            (            $Number            in            1..15            | Where {            $_            %            2            } ) {            "Outer:              $Number"            foreach            ($x            in            'a',            'b',            'c') {            if            ($Number            -gt            5) {            continue            OUTER         }            $x            *            $Number            } }                  

As you can see in the above example, every time the number is greater-than 5 the flow of execution jumps to the outer loop because of the Continue OUTER which indicates it should got to the outer loop. Hence, the next line is not executed.

Result:

The Continue And Break Keywords in Foreach Loop

To skip processing of the remaining block and move on to the next iteration in the loop, you can use the keyword continue. Here is a quick example to demonstrate that:

Example :

                      $Numbers            =            4..7            foreach            ($Num            in            1..10) {            if            ($Numbers            -Contains            $Num) {            continue            }            $Num            }                  

Result:

If you use the keyword break, you will break out of the (innermost) loop entirely (unless you specify a label) like you can observe in the following example.

Example :

                      foreach            ($Num            in            1..10) {            if            ($Numbers            -Contains            $Num) {            break            }            $Num            }                  

Result:

If you have nested loops that aren't labelled, continue and break will act on the innermost block, as demonstrated in the following example:

                      foreach            ($Num            in            1..3) {            foreach            ($Char            in            'a','b','c') {            if            ($Num            -eq            2) {            continue            }            $Char            *            $Num            } }                  

Result:

The Special $foreach Variable

In a Foreach loop we can utilize this special variable called $foreach which is an enumerator to iterate through values processed by their containing code block, which is Foreach loop in our case. Read more about Using Enumerators here.

$foreach variable gives you MoveNext() method which can skip and advance the enumerator to the next element of the collection and a property called current to access the current value that in the loop.

Note: MoveNext() method returns probably want to suppress by piping to Out-Null, assigning to the $null variable or casting it to the [void] type accelerator).

Following example demonstrates, how to use this method to return odd numbers between 1 and 10:

Example :

                      foreach            ($Int            in            1..10) {            $Int            $foreach.MoveNext() |            Out-Null            }                  

Result:

Another use case might be if you have an array and want to skip some items in collection depending upon a condition. Then like in the following example, MoveNext() method can be used to skip the element and advance to the next item in the collection.

Example :

                      $Array            = ('a','b','c','d','e','f','g')            foreach            ($Letter            in            $Array) {            if            ($Letter            -eq            'c') {            # skips one element            $null            =            $foreach.MoveNext()            # c            $foreach.Current            break            } }            foreach            ($Letter            in            $Array) {            if            ($Letter            -eq            'c') {            # skips two element            $null            =            $foreach.MoveNext()            # c            $null            =            $foreach.MoveNext()            # d            $foreach.Current            break            } }                  

Result:

In the above example, once the current item is an alphabet 'c' then enumerator advances to the next item which is 'd' and to print this we use the property of $foreach enumerator called Current that will return the current element 'd'.

To suppress the unwanted boolean output from $foreach.MoveNext(), you can also cast the results to the [void] type accelerator:

          [void]            $foreach.MoveNext()                  

Let's take a real-world example where you have an array of values and where every other element can be used as a key and a value in a hashtable. To add these elements of an [array] to a [hashtable], you can use something like this.

Example :

                      $Array            = @(            'Name',            'John',            'Surname',            'Doe',            'Occupation',            'Homeless')            $Hash            = @{}            foreach            ($Temp            in            $Array) {            $Key            =            $Temp            # move to next element            [void]            $foreach.MoveNext()            $Value            =            $foreach.Current            $Hash.Add($Key,            $Value) }            $Hash                  

Result:

$Foreach also has a Reset() method, which can set the enumerator it initial position, i.e, before the first element in the collection. Like in the following example, once the value of a variable $i -eq 3 we reset() the enumerator, so the loop again start enumerating from the element of the collection and since there is no exit condition it will be stuck in an infinite loop, iterating between numbers 1 to 3.

                      foreach($i            in            1..5) {            $foreach.current            if($i            -eq            3){            Write-Host            "Reset Enumerator`n"            $foreach.reset()     } }                  

Result:


FOREACH-OBJECT CMDLET

ForEach-Object is a cmdlet (Not a loop) but just like a Foreach statement, it iterates through each object in a collection of an object and performs operations on it.

A collection can be passed to the -InputObject parameter of the cmdlet or can be piped to it. Whatever you choose the output is the same in both the cases.

Let's understand this with the following example, where input objects are passed to InputObject parameter:

Example 22:

                      $process            =            Get-Process            |            Select-Object            -First            10            #collection of objects            Foreach-Object            -InputObject            $process            -Process {$_.Name}            # alternatively            Foreach-Object            -InputObject            $process            {$_.Name}                  

Result:

When Input objects are passed to cmdlet through the pipeline:

Example 23:

                      $process            =            Get-Process            s* |            Select-Object            -First            10            $process            |            Foreach-Object            -Process {$_.Name}                  

Result:

In above example, curly braces '{}' is a scriptblock that is basically passed to the -Process parameter of this cmdlet, also known as Process block which iterates each item of the collection passed to Foreach-Object cmdlet. You don't have to explicitly specify -Process parameter before curly braces as it is the expected value at position '0'.

Let's do Get-Help to this cmdlet and see few parameters and how they can be used with Foreach-Object cmdlet (Parameters are InputObject, Process and MemberName parameter)

Example 24:

                      Get-Help            Foreach-Object            -Full                  

This screenshot says about 2 parameter sets of Foreach-Object cmdlet and both set have one mandatory parameter i.e. -MemberName and -Process parameter respectively and one optional parameter -InputObject in each parameter set.

Below are the explanation of those 3 parameters:

InputObject – It accepts value via pipeline or can pass the complete object as a value to this parameter

Process – It accepts scriptblock as a value and it is a positional parameter

MemberName – It either accepts property name or method of object as a value and it is also a positional parameter

Basically, there are two different ways to construct a ForEach-Object command:

  1. Using Scriptblock: You can use a script block to specify the operation. Within the script block, use the $_ or $PSItem variable to represent the current object. The script block is the value of the Process parameter which is the mandatory parameter when a collection is passed either to InputObject parameter or via pipeline. The script block can contain any Windows PowerShell script.

Example 25:

                      # script block with pipeline input            1..5|Foreach-Object            -Process {$_}            # script block with InputObject parameter            Foreach-Object            -InputObject (1..5) -Process {$_}                  

Results:

  1. Using Operation Statement: Operation statements were introduced in Windows PowerShell 3.0. You can also write an operation statement, which is much more like a natural language. You can use the operation statement to specify a property name or call a method available for each object in a collection. Both Property name and method name are passed as a value to 'MemberName' parameter. You can also use ArgumentList parameter of Foreach-Object cmdlet if you are invoking a method on an object and it requires some arguments to pass (multiple arguments can also be passed to ArgumentList parameter).

For example, the following command also gets the value of the ProcessName property of each process on the computer.

Example 26:

                      # MemberName parameter that accepts 'ProcessName' property of each object            # within a collection as a value (input passed through pipeline)            Get-Process            a* |            ForEach-Object            -MemberName ProcessName                  

Result:

          ApplicationFrameHost armsvc                  

In this example, we call a method Split() on each string object that are passed through pipeline to Foreach-Object cmdlet and this method accepts dot (.) as an argument which would be passed as a value to ArgumentList parameter so that this method will split the each string object with dot(.)

Example 27:

                      # checking split() method through get-member cmdlet            "Microsoft.PowerShell.Core",            "Microsoft.PowerShell.Host"|            Get-Member            -Name Split                  

As shown in the above screenshot, a Split() method is available, that we can invoke on each string object. Following example demonstrates how it can be used:

Example 28:

                      "Microsoft.PowerShell.Core",            "Microsoft.PowerShell.Host"            | `            ForEach-Object            -MemberName Split -ArgumentList            "."                  

Result:

Foreach-Object does not support Break and Continue branching statements. Let's understand the behavior of Continue keyword in Foreach-Object cmdlet in the following example.

The following piece of code should print 1 : a, 1: b, 1:d and similarly for all other combinations. But, if you will observe the results, you will notice that after 1:b it is proceeding to 2:a without processing 1:d. That proves that continue statement inside a Foreach-Object will bring the flow of control to the outer for loop instead of the foreach-Object cmdlet.

Example 29:

                      for($i=1;$i            -le            3;            $i++){            "a","b","c","d"            |            foreach-object            {            if($_            -eq            "c") {            continue            }            write-host            "$i              :              $_"            }              }                  

Result:

ForEach-Object has two aliases, ForEach and% and the following three examples demonstrate this as they are identical in function, they will return the same output results.

Example 30:

                      Get-WMIObject            Win32_LogicalDisk |            ForEach-Object            {[math]::Round($_.FreeSpace/1GB,2)}            Get-WMIObject            Win32_LogicalDisk |            ForEach            {[math]::Round($_.FreeSpace/1GB,2)}            Get-WMIObject            Win32_LogicalDisk | % {[math]::Round($_.FreeSpace/1GB,2)}                  

Result:

                      197.7            195.21                  

Difference between Foreach and Foreach-Object

Foreach Foreach-Object
It loads all of the items into a collection before processing them one at a time Processes only one item at a time through the pipeline
High memory utilization, because whole collection is loaded Low memory utilization, since once item is processed at a time
If we are dealing with small collection of objects, it is always faster than Foreach-Object cmdlet Comparatively slower than foreach loop
Cannot pass collection of objects to Foreach loop via pipeline. And even if you use it after a pipe it will become Foreach-Object, because Foreach is also an alias for Foreach-Object cmdlet Collection of objects can either be passed through pipeline or to InputObject parameter
As pipeline is not being used by this, it won't allows you to pass the objects to another command via pipeline We can easily chain commands and pass the output from Foreach-Object cmdlet to another command via pipeline
Supports keywords like Break and Continue Doesn't support Break and Continue and you can not expect a behavior like in a normal loop

Performance Foreach vs Foreach-Object

When we measure the performance of Foreach loop and Foreach-Object for a small set of data we can clearly see, that Foreach loop is faster.

Example 31:

                      Measure-Command            -Expression {Get-WMIObject            Win32_LogicalDisk |            ForEach-Object            {[math]::Round($_.FreeSpace/1GB,2)}}                  

Result:

          Days              :            0            Hours             :            0            Minutes           :            0            Seconds           :            0            Milliseconds      :            87            Ticks             :            872447            TotalDays         :            1.00977662037037E-06            TotalHours        :            2.42346388888889E-05            TotalMinutes      :            0.00145407833333333            TotalSeconds      :            0.0872447            TotalMilliseconds :            87.2447                  

Example 32:

                      Measure-command            -Expression {            $disks            =            Get-WMIObject            Win32_LogicalDisk            foreach($disk            in            $disks){        [math]::Round($disk.FreeSpace/1GB,2)      } }                  

Result:

          Days              : 0 Hours             : 0 Minutes           : 0 Seconds           : 0 Milliseconds      : 34 Ticks             : 348787 TotalDays         : 4.03688657407407E-07 TotalHours        : 9.68852777777778E-06 TotalMinutes      : 0.000581311666666667 TotalSeconds      : 0.0348787 TotalMilliseconds : 34.8787                  

Note: When dealing with large sets of items, Foreach-Object cmdlet might perform faster and in a memory efficient way with the use of pipelines, as it has not to wait for the entire loop to complete like 'Foreach' and quickly pass the items to the next step in the pipeline as and when they are processed.


Foreach Method

With the release of Windows PowerShell 4.0, a new Magic Method was introduced to support DSC that only works with collection known as Foreach() Method. This provides new syntax for accessing Foreach loop capabilities in Windows PowerShell. It allows you to rapidly loop through a collection of objects/array and execute a block of statement against each object in that collection.

All you have to do is specify a collection or an array like @(1,2,3) and dot(.) operator is used to access the Foreach() method. The ForEach() method accepts a PowerShell ScriptBlock as arguments inside the parenthesis that is executed once for each object in the collection. Within this ScriptBlock, the current object for an iteration can be referred to using the $_ or $PSItem automatic variables. You also get the ability to pass arguments to the [scriptblock] if required, which we will understand with other supported ways to invoke Foreach() method later in this article.

Syntax:

          Collection.Foreach({scriptblock})  Input - A collection of objects e.g services, integers, server names etc.  Output - System.Collections.ObjectModel.Collection1[psobject] (Outputs are returned in a generic collection of this type.)                  

Example 33:

                      # data type is array that can be passed as a collection to Foreach() method            (1..5).GetType                  

Result:

          IsPublic IsSerial Name       BaseType -------- -------- ----       -------- True     True     Object[]   System.Array                  

Example 34:

                      # using range operator to define the collection                        # and passed it as an input to Foreach method            (1..5).Foreach({$_})            # using $_ to access current object            # alternatively            (1..5).Foreach({$PSItem})            # using $PSItem to access current object                  

Result:

                      1            2            3            4            5                  

The output generated from Foreach() method is a collection data type, as demonstrated in the following example.

Example 35:

          ((1..5).foreach({$_})).GetType()                  

Result:

          IsPublic IsSerial Name          BaseType -------- -------- ----          -------- True     True     Collection`1            System.Object                  

NOTE: It's important to ensure that you are working with an array or collection, and not a single object. You will receive an error when trying to call the Foreach() method on a single object, because it only works on arrays/collection. If it's possible for a command to return a single object, then as a best practice, you should wrap the results in @() to ensure that PowerShell treats the results as an array, even if only a single object is returned.

For example, a single process of notepad.exe is running on your machine that means if you run Get-Process cmdlet on notepad.exe the you will get single process object, which is not a collection and hence you would not be able to find Foreach() method by using dot(.) operator. But, you can easily work around that by wrapping the complete expression in @() to treat this as a collection/array.

Example 36:

                      # single notepad process            (Get-Process            -Name notepad).GetType()                  

Result:

          IsPublic IsSerial Name     BaseType -------- -------- ----     -------- True     False    Process  System.ComponentModel.Component                              

Whereas, when you make it an array you will find the Foreach() method through IntelliSense.

Example 37:

          @(Get-Process            -Name notepad).GetType()                  

Result:

          IsPublic IsSerial Name      BaseType -------- -------- ----      -------- True     True     Object[]  System.Array                  

In the next example, we have multiple chrome.exe processes, which means it is an array of objects or a collection of chrome process objects. So here you don't have to explicitly wrap the expression in @().

Example 38:

                      # multiple chrome processes            (Get-Process            -Name chrome).GetType()                  

Result:

          IsPublic IsSerial Name      BaseType -------- -------- ----      -------- True     True     Object[]  System.Array                  

The Foreach() method is called as Magic Method because it doesn't show up Get-Member output, even if you apply -Force and request-MemberType All or when you try to access intrinsic methods\properties of a PSObject like .psobject.Methods. That is because it is a private extension method implemented on a private class. But when you use dot(.) operator with collection then you are able to see the method.

Example 39:

                      1..5|Get-Member            -Force                  

This new method works very similarly to the ForEach-Object cmdlet that has existed in PowerShell since the beginning. They merely provide an alternate syntax for PowerShell developers who are hoping to use a fluent-style syntax in their code.

Ways to use Foreach() Method

  1. collection.ForEach(scriptblock_expression)
  2. collection.ForEach(scriptblock_expression, object[] arguments)
  3. collection.ForEach(type convertToType)
  4. collection.ForEach(string propertyName)
  5. collection.ForEach(string propertyName, object[] newValue)
  6. collection.ForEach(string methodName)
  7. collection.ForEach(string methodName , object[] arguments)

NOTE: These are supported argument pairings, not different overloads available for the ForEach method. Using any argument pairings other than these may result in errors that do not clearly identify what the actual problem is.

Method 1 – ForEach(scriptblock expression)

If you pass a script block expression into the ForEach() method, you are able to perform the same set of operations defined in the scriptblock on each item in the collection, just like the foreach statement or the ForEach-Object cmdlet.

Example 40:

                      # get a collection of services            $services            =            Get-Service            sa*            # display the service status & names in the collection            $services.foreach({            if($_.Status            -eq            "Running"){            $_.DisplayName     }            else{            "$($_.DisplayName)[stopped]"            } })                  

Result:

Method 2 – ForEach(scriptblock expression, object[] arguments)

Any arguments that you provide beyond the initial script block, will be used as arguments for the script block. This is just like how the -ArgumentList parameter works on the -Process parameter of ForEach-Object cmdlet.

Example 41:

                      # get a collection of services                        $services            =            Get-Service            sa*            # select a property name to expand                        # using a script block argument            $services.ForEach({            Param($Name,$Status)     [pscustomobject]@{         DisplayName=$_.$Name            Status=$_.$Status            }     },'DisplayName','Status')                  

Result:

Method 3 – ForEach(type convertToType)

You can also pass a data type into the ForEach() method and convert every item in a collection into another data type. For example, imagine you have a collection of [int] objects and you want to convert those objects into their [string] equivalent.

Since we have [int] values in the collection, that means we can also perform addition on each item of the collection.

Example 42:

          (1..5).foreach({$_+2})                  

Result:

          3 4 5 6 7                  

and now convert each [int] item of the collection to [String] using the following approach.

Example 43:

                      # type conversion from System.Int32 to System.String            $result1=(1..5).foreach([string])            #the data type of each object in the collection is changes to System.String            $result1            |            Get-Member                  

Once [int] is converted to [string], now you can also per perform string operations like, concatenation on each object in the collection.

Example 44:

                      $result1.foreach({$_+2})                  

Result:

                      12            22            32            42            52                  

Method 4 – ForEach(string propertyName)

You can iterate through a collection of objects and return a value for a particular property of an object within that collection. In this case, it will only work against a single property name as an argument, any exception will throw an error. To list more than one property, we have to use param block just like we have seen in the previous sub-section (2nd supported the way to define Foreach() method).

Example 45:

                      # return the names of those services which starts from we            (Get-Service            we*).foreach('Name')                  

Result:

          WebClient Wecsvc WEPHOSTSVC wercplsupport WerSvc                  

Method 5 – ForEach(string propertyName, object[] newValue)

Not only we can fetch the values of a property of each item within the collection, but we can also set a value to the property of each item. This is functionality that is not available in loops like foreach and Foreach-Object, unless you explicitly create the script block to achieve that. To set the property, you simply provide the property name and the new value, as an arguments in Foreach() method, like in the following example. PowerShell will attempt to convert the new value you have provided as an argument and set the value for the property for each item in the collection.

Example 46:

                      # get display name of service whose name starts with co            $service            = (Get-Service            co* |            Select-Object            -Property DisplayName)            # Now change the display names of every service to some new value            $service.foreach('DisplayName','Hello')            $service                  

Result:

          DisplayName                            -----------                            Hello  Hello                                  Hello                  

NOTE: Only the value for the respective property of each item in collection is changed, and the underlying [System.ServiceProcess.ServiceController] objects are not changed in anyway.

Method 6 – ForEach(string methodName)

Methods of an object in the collection can also be invoked using the Foreach() method. You just simply provide the method name as the argument to the foreach method and it will be invoked without any arguments. Just make sure that if you are passing only method name in foreach() method, then the method does not accept any argument.

Here's an example showing how you could kill a bunch of processes running a specific program by using a kill() method as an argument to Foreach() method:

Example 47:

          Start-Process -FilePath  Notepad.exe (Get-process            -name            'notepad')|Get-Member                  

Example 48:

                      # use 'kill' method to kill the process of notepad            (Get-process            -name            'notepad').ForEach('Kill')                  

Method 7 – ForEach(string methodName, object[] arguments)

A method along with arguments can also be passed to a foreach() method. In below example, we get list of all commands which have computername parameter and then we pass this collection as an input to foreach method along with 2 arguments: ResolveParameter (a method for an object) and ComputerName (an argument to this method).

Example 49:

                      $cmds            =            Get-Command            -ParameterName ComputerName            # get the methods and properties of object in which you can see ResolveParameter method            # which will be used in Foreach() method            $cmds            |            Get-Member                  

Result:

Example 50:

                      # Now show a table making sure the parameter names and aliases are consistent            $cmds.foreach('ResolveParameter','ComputerName') |            Format-Table            Name, Aliases                  

Result:

          Name         Aliases ----         ------- ComputerName {} ComputerName {Cn} ComputerName {Cn} ComputerName {cn} ComputerName {} ComputerName {Cn} ComputerName {Cn} ComputerName {CN, __Server, IPAddress} ComputerName {Cn} ComputerName {Cn} ComputerName {Cn} ComputerName {Cn} ComputerName {CN}                  

Differences between Foreach-Object and Foreach() Method

Foreach-Object Foreach Method
Collection is passed either via pipeline or to -InputObject parameter Foreach() method works on collection with the help of dot(.) operator
It is a PowerShell cmdlet It is a method introduced with PowerShell v4.0
This has parameters such as -Begin and -End for refining your script, and also -Confirm and -WhatIf for testing Foreach() method accepts certain types of arguments only as we have seen in above sub-section

Optical Character Recognition
~ Author of "PowerShell Guide to Python",  and currently writing a Book on "Windows Subsystem for Linux (WSL)"


All my books are available as a discounted bundle:

    1. PowerShell Guide to Python :ThisPowerShell Scripting guide to Python is designed to make readers familiar withsyntax, semantics and core concepts of Python language, in an approach thatreaders can totally relate with the concepts of PowerShell already in their arsenal, to learn Python fast and effectively, such that itsticks with readers for longer time.
    2. Windows Subsystem for Linux (WSL) : Keywords, definitions, and problems WSL solve and how it works under the hoods. From download to setup to interoperability this book even covers details like the architecture of Windows subsystem for Linux and new features in WSL 2 with some wonderful use cases.