Foreach - from PowerShell in depth

Original author: Don Jones, Jeffery Hicks, Richard Siddaway
  • Transfer
Chapter 19.3. It describes the design of Foreach, how to apply it.

This construct has the same goals as the ForEach-Object cmdlet. The ForEach-Object cmdlet has a ForEach alias that is easy to confuse with the ForEach statement ... because they have exactly the same name. PowerShell looks at the context to find out which Foreach is currently in use. Here is an example of how the operator and the cmdlet do the same thing
Get-Service –name B* | ForEach { $_.Pause() }
$services = Get-Service –name B*
ForEach ($service in $services) 
{
    $service.Pause()
}


Let's see how this Foreach statement works, it has two variables in brackets, separated by the in keyword. The second variable is expected to contain one or more objects with which we want to do something. The first variable for internal use, it will contain in turn in each pass the object from the second variable. If you wrote in VBScript then this kind of thing should look familiar to you.

It is common practice to name the second variable in the plural, and the first in the singular. This is not required by convention or standard, although you can write Foreach ($ Fred in $ Rocvill) provided that $ Rockvill contains objects, PowerShell will be happy to handle this. But stick to the deliberate naming of variables, then you will have to remember much less.

PowerShell automatically takes one object from the second variable and places it in the first on each pass of the loop. Inside the construct, you can use the first variable to do something with the object, for example, you can call the Pause method of this object. Do not use $ _ (or PSItem) in the statement, as you do in the cmdlet.

Sometimes, you may not be sure which design to use - an operator or a cmdlet. Theoretically, piping to a Foreach-object may use less memory in some situations. According to our observations, the cmdlet runs slower with large sets of objects. If you have complex processing in the pipeline, especially if the Foreach statement is being processed inside the Foreach cmdlet, it is better to use the full name to uniquely determine what exactly is being used.

You also need to be careful if you want to make a pipeline transfer to another cmdlet or function. Example
PS C:\> foreach ($service in $services) {
>>                $service | select Name,DisplayName,Status
>>  } | Sort Status
>>
An empty pipe element is not allowed.
At line:3 char:4
+ } | <<<< Sort Status
+ CategoryInfo : ParserError: (:) [],
ParentContainsErrorRecordException
+ FullyQualifiedErrorId : EmptyPipeElement

PowerShell threw an error because there is nothing to pass further down the pipeline to Sort, but this example will work:
PS C:\> $services | foreach {
>> $_ | select Name,DisplayName,Status
>> } | Sort Status
>>
Name DisplayName Status
---- ----------- ------
Browser Computer Browser Stopped
BDESVC BitLocker Drive Encrypt... Stopped
bthserv Bluetooth Support Service Running
BFE Base Filtering Engine Running
BITS Background Intelligent ... Running

Another factor is whether you will need to use the collection of objects again, if you need a further collection, it is better to use the operator, so as not to receive data again. The last point is that many of the scripts that you find on the Internet are actually VBScript conversion scripts; you constantly had to sort through collections of objects in it. Therefore, it is always worth stopping and thinking for a second to determine the best approach and whether it is necessary to sort out the collection at all.

Our advice - do not use busting if there is a possibility not to do this. For example, we rewrite our code given earlier in another way:
Get-Service –name B* | Suspend-Service

and the sorting example would look much better if you write it like this:
Get-Service b* | Sort Status | select Name,DisplayName,Status

If you do not need to sort through all the objects for something, do not do this. Using a Foreach cmdlet or statement is sometimes a sign that you are doing something that should not be done. This is certainly not true in all cases, but consider whether PowerShell can do some of the work for you. Do not dwell on this issue, many cmdlets simply do not accept the output from the assembly line for parameters that you may need, in which case using Foreach becomes a necessity. It is more important to finish the job than to track correctly or incorrectly you wrote something.
translator insert
refers to the ability of a posik to bind together the parameters of objects on the conveyor by parameter names. If there is a chance that the send will not be able to uniquely identify the parameter of the object in the process of "parameter binding", then you will have to sort through the objects one at a time. For example WMI. General recommendation - use pipeline processing, the cycle usually interrupts the pipeline, and sometimes leads to unnecessary iterations. For example, it is more efficient to do Select and then processing than if inside Foreach and processing

Good point to remind you of the brackets. We lied when we said that the Foreach operator needs two variables. From a technical point of view, only one variable is needed for this. The second should contain a collection of objects that can either be in a variable as in our examples before, or can be the result of an expression in brackets, for example:
foreach ($service in (Get-Service –name B*)) {
$service.pause()
}

This version is harder to read, but is completely legal, eliminating the need to store intermediate results in a variable. Inner brackets are calculated first and produce a collection of objects that is passed on.

Also popular now: