📜 ⬆️ ⬇️

Foreach - from the book PowerShell in depth

Chapter 19.3. It describes the construction 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, which is easily confused with the ForEach operator ... because they have exactly the same name. PowerShell looks at the context to find out which Foreach is being applied now. Here is an example of how the statement 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 us analyze how this Foreach operator works, it has two variables in brackets, separated by the keyword in. The second variable, as expected, contains 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 on VBScript then this kind of thing should look familiar to you.

The usual practice is 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 conscious naming of variables, then you have to memorize much less.
')
PowerShell automatically takes one object from the second variable and places it in the first one on each pass of the loop. Inside the construction, 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 construct to use — an operator or cmdlet. Theoretically, passing through a conveyor to a foreach object can use less memory in some situations. According to our observations, the cmdlet runs slower with large sets of objects. If you have complicated processing in the pipeline, especially if processing is performed by the Foreach operator within the Foreach cmdlet, it is better to use the full name to uniquely determine what is being used.

You also need to be careful if you want to pass along the conveyor 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 gave an error, because there is nothing to pass further along the conveyor 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 further collection then it is better to use the operator in order not to receive data again. The last point is that many of the scripts that you find on the Internet are in fact a conversion of VBScript scripts, it constantly had to iterate over collections of objects. Therefore, you should always stop and think for a second to determine the best approach and whether it is necessary to go through the collection in general.

Our advice - do not use brute force if it is possible not to do it. For example, let's rewrite our code presented earlier in another way:
 Get-Service –name B* | Suspend-Service 

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

If you don’t need to sort through all the objects, don’t do it. Using the foreach cmdlet or operator is sometimes a sign that you are doing something that should not be done. This is certainly not true in all cases, but think about whether PowerShell can do some of the work for you. Do not focus on this issue, many cmdlets simply do not accept output from the pipeline for the parameters that you may need, in which case using Foreach becomes a necessity. It is more important to finish the work than to track correctly or incorrectly you wrote something.
insert translator
refers to the ability to link together the parameters of objects on the pipeline by parameter names. If there is a possibility that the wire will not be able to unambiguously identify the parameter of the object in the process of “parameter binding”, then it will be necessary to sort the objects one by one. For example, WMI. General recommendation - use pipeline processing, the loop 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, this requires only one variable. The second one should contain a collection of objects that can be either 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. Internal brackets are evaluated first and produce a collection of objects that is passed on.

Source: https://habr.com/ru/post/278701/


All Articles