A Loop That Keeps Continues Iterating Until a Special Value is Entered is Called a
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
- FOR Loop
-
For
Loop with multiple repeat placeholder - Multiplication and other operations in Repeat placeholder of
For
Loop - Nested
For
Loop - Infinite
For
Loop
-
- WHILE Loop
- DO-WHILE Loop
- Difference Between
While
andDo-While
Loop - DO-UNTIL Loop
- Difference between
Do-While
andDo-Until
- 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 withLabel
- The
Continue
AndBreak
Keywords inForeach
Loop - The Special
$foreach
Variable
- How PowerShell
- Foreach-Object Cmdlet
-
[scriptblock]
with pipeline input -
[scriptblock]
with InputObject parameter - Difference between
Foreach
andForeach-Object
-
- Performance
Foreach
vsForeach-Object
-
Foreach
Method - 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)
- Differences between
Foreach-Object
andForeach()
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 –
-
For
Loop -
While
loop -
Do-While
loop -
Do-Until
loop -
Foreach-Object
cmdlet -
Foreach
Statement -
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:
-
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 theProcess
parameter which is the mandatory parameter when a collection is passed either toInputObject
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:
-
Using Operation Statement
: Operation statements were introduced inWindows 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 useArgumentList
parameter ofForeach-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
- collection.ForEach(scriptblock_expression)
- collection.ForEach(scriptblock_expression, object[] arguments)
- collection.ForEach(type convertToType)
- collection.ForEach(string propertyName)
- collection.ForEach(string propertyName, object[] newValue)
- collection.ForEach(string methodName)
- 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 |
~ 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:
-
- 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.
- 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.
Source: https://ridicurious.com/2019/10/10/powershell-loops-and-iterations/
0 Response to "A Loop That Keeps Continues Iterating Until a Special Value is Entered is Called a"
Post a Comment