Reputation: 377
Where-Object
, Select-Object
and ForEach-Object
I am a PowerShell beginner. I don't understand too much. Can someone give examples to illustrate the differences and usage scenarios between them?
Upvotes: 4
Views: 8711
Reputation: 1140
You have two things in there: filtering and iterating through a collection.
Get-Process | where-Object {$_.Name -like 'chrome'} | Export-Csv 'c:\temp\processes.csv'
Get-Process -Name chrome | Export-Csv c:\temp\processes.csv
This is great when working with huge lists of computers or big files.
Many commandlets have their own filtering capabilities. Run get Get-Help get-process -full
to see what they offer before piping.
Here you have 3 possibilities:
Get-Service -name BITS,Spooler,W32Time | Set-Service -startuptype Automatic
gwmi win32_networkadapterconfiguration -filter "description like '%intel%'" | EnableDHCP()
Get-WmiObject Win32_Service -filter "name = 'BITS'" | ForEach-Object -process { $_.change($null,$null,$null,$null,$null,$null,$null,"P@ssw0rd") }
Credits: I found explanations that cleared the mess in my head around all these things in a book called : Learn Powershell in a month of lunches (chapters 9 and 13 in this case)
Upvotes: 3
Reputation: 638
If you are at all familiar with either LINQ or SQL then it should be much easier to understand because it uses the same concepts for the same words with a slight tweak.
Where-Object
is used for filtering out objects from the pipeline and is similar to how SQL filters rows. Here, objects are compared against a condition, or optionally a ScriptBlock
, to determine whether it should be passed on to the next cmdlet in the pipeline. To demonstrate:
# Approved Verbs
Get-Verb | Measure-Object # count of 98
Get-Verb | Where-Object Verb -Like G* | Measure-Object # 3
# Integers 1 to 100
1..100 | Measure-Object # count of 100
1..100 | Where-Object {$_ -LT 50} | Measure-Object # count of 49
This syntax is usually the most readable when not using a ScriptBlock
, but is necessary if you want to refer to the object itself (not a property) or for more complicated boolean results. Note: many resources will recommend (as @Iftimie Tudor mentions) trying to filter sooner (more left) in the pipeline for performance benefits.
Select-Object
is used for filtering properties of an object and is similar to how SQL filters columns. Importantly, it transforms the pipeline object into a new PSCustomObject
that only has the requested properties with the object's values copied. To demonstrate:
Get-Process
Get-Process | Select-Object Name,CPU
Note, though, that this is only the standard usage. Explore its parameter sets using Get-Help Select-Object
where it has similar row-like filtering capabilities like only getting the first n objects from the pipeline (aka, Get-Process | Select-Object -First 3
) that continue onto the next cmdlet.
ForEach-Object
is like your foreach
loops in other languages, with its own important flavour. In fact, PowerShell also has a foreach
loop of its own! These may be easily confused but are operationally quite different. The main visual difference is that the foreach
loop cannot be used in a pipeline, but ForEach-Object
can. The latter, ForEach-Object
, is a cmdlet (foreach
is not) and can be used for transforming the current pipeline or for running a segment of code against the pipeline. It is really the most flexible cmdlet there is.
The best way to think about it is that it is the body of a loop, where the current element, $_
, is coming from the pipeline and any output is passed onto the next cmdlet. To demonstrate:
# Transform
Get-Verb | ForEach-Object {"$($_.Verb) comes from the group $($_.Group)"}
# Retrieve Property
Get-Verb | ForEach-Object Verb
# Call Method
Get-Verb | ForEach-Object GetType
# Run Code
1..100 | ForEach-Object {
$increment = $_ + 1
$multiplied = $increment * 3
Write-Output $multiplied
}
Upvotes: 10