Flowing objects between command lines with the pipeline: the object-not-text concept, filtering with Where-Object, property selection with Select-Object, sorting with Sort-Object, grouping with Group-Object, aggregation with Measure-Object, plus parallel ForEach-Object in PowerShell 7.

In episode 7 you built functions that accept input and call other commands. In episode 6 you processed collections with ForEach-Object. Now it's time to understand what connects it all: the pipeline. The pipeline is the heart of PowerShell — and like a heart, its workings are often misunderstood by beginners.
Many assume the pipeline flows text — far from it. This misconception is what leads people to write regex expressions to process output that should just be read as object properties. Episode 8 sets things straight: the pipeline flows objects, and objects carry structure that can be filtered, selected, sorted, and grouped.
When you run Get-Process, what appears on screen is a neat table. But that table is just a printout — behind it are objects with properties like ProcessName, CPU, WS, and callable methods. The screen shows a representation; the pipeline carries the real objects.
Get-Process pwsh | Select-Object ProcessName, Id, CPU, WS
Get-Process pwsh | Get-MemberGet-Member unpacks the object structure: the list of properties, methods, and types. This is the key to exploration — when you're stuck wondering "what properties are available?", Get-Member is the answer.
The pipeline chains commands with |. The left command's output becomes the right command's input. Inside cmdlets like Where-Object and ForEach-Object, the $_ variable points to the object currently being processed:
Get-Process |
Where-Object { $_.WS -gt 100MB } |
Sort-Object WS -Descending |
Select-Object ProcessName, Id, WSRead it like a sentence: "take all processes, keep only those using more than 100 MB, sort descending, then pick the columns". Each stage receives objects, processes them, and passes them on.
Where-Object filters — it passes only objects that meet the condition:
Get-Service | Where-Object { $_.Status -eq "Running" }
Get-Process | Where-Object { $_.CPU -gt 10 }PowerShell 3 and above provide a more concise simplified syntax: property, operator, value — without $_ and the braces:
Get-Service | Where-Object Status -eq "Running"
Get-Process | Where-Object WS -gt 100MB
Get-ChildItem /data | Where-Object Extension -in @(".log", ".txt")The simplified syntax reads like a natural filter and is shorter. Save the full syntax for complex conditions involving expressions or multiple properties at once.
Select-Object picks the properties you want — trimming objects into a concise "identity card":
Get-Process pwsh | Select-Object ProcessName, Id, WSSeveral frequently used modifiers:
-First, -Last, -Skip — limit the number of items or skip some.-Unique — removes duplicates.-ExpandProperty — takes a property value as a raw value, not an object.Get-Process | Select-Object -First 5 ProcessName, Id
Get-Process | Select-Object -ExpandProperty ProcessName
Get-Service | Select-Object -ExpandProperty Name | Select-Object -UniqueThe difference between Select-Object Name and Select-Object -ExpandProperty Name matters: the former produces a wrapped object with the Name property, the latter produces a plain string. -ExpandProperty is used when the next stage wants the raw value — for example as the -Name for another cmdlet.
To create a new column from a calculation, use a hashtable: the column name in the Name key, the expression in the Expression key:
Get-Process pwsh |
Select-Object ProcessName,
@{Name = "MemoriMB"; Expression = { [math]::Round($_.WS / 1MB, 1) }},
@{Name = "CPUIntensif"; Expression = { $_.CPU -gt 50 }}This code adds a MemoriMB column from a calculation and a CPUIntensif column from a comparison. The hashtables are all written inside code blocks — outside the code, just remember the concept: Name for the label, Expression for the formula.
Sort-Object sorts by one or more properties:
Get-ChildItem /data -File | Sort-Object Length -Descending
Get-Service | Sort-Object Status, NameMultiple properties are sorted in sequence: Status first, then Name for those with the same status. -Descending reverses the order.
Group-Object gathers objects with the same property value — like grouping cards by suit:
Get-Service | Group-Object Status
Get-ChildItem /data -File | Group-Object ExtensionThe output shows Name (the group value), Count (number of members), and Group (the member list). From one line you know the distribution of service statuses or the file types in a folder.
Measure-Object sums up numeric data — total, average, min-max values:
Get-ChildItem /data -File | Measure-Object Length -Sum -Average -Maximum -Minimum
Get-Service | Measure-ObjectWithout a numeric property, Measure-Object counts the number of items — useful as a replacement for Count inside a pipeline.
Most pipelines process items one by one. PowerShell 7 adds -Parallel: processing many items concurrently — like a conveyor belt that now has many lanes:
$urls = @("https://a.example", "https://b.example", "https://c.example")
$urls | ForEach-Object -Parallel {
$r = Invoke-WebRequest $_ -UseBasicParsing
Write-Output "$_ -> $($r.StatusCode)"
} -ThrottleLimit 5Two important notes. First, -ThrottleLimit limits the number of parallel tasks so the system isn't overloaded. Second, the parallel block runs in a separate runspace — outer variables aren't visible; send values via $using: if needed.
Warning
Parallelism speeds up work that waits on I/O — like network requests — but it's not a universal cure. For pure computation work, speed can be lost to runspace overhead. Measure first: if the workload waits on the network or disk, -Parallel helps; if it just spins the CPU, consider the regular order.
Chain all the concepts together: a report of the most memory-hungry processes on the system.
Get-Process |
Where-Object { $_.WS -gt 50MB } |
Sort-Object WS -Descending |
Select-Object -First 5 ProcessName, Id,
@{Name = "MemoriMB"; Expression = { [math]::Round($_.WS / 1MB, 1) }} |
Format-Table -AutoSizeEpisode 8 reveals how the pipeline works: objects flow between lines, not text; $_ points to the object being processed; Where-Object filters with both full and simplified syntax; Select-Object selects, limits, removes duplicates, expands properties, and computes new columns via calculated properties; Sort-Object sorts; Group-Object groups; Measure-Object aggregates; and ForEach-Object -Parallel speeds up I/O-waiting work in PowerShell 7.
Key takeaways:
Get-Member to unpack their structure.Where-Object filters, Select-Object shapes, the rest arranges.Where-Object syntax is more concise for simple filters.-Parallel for I/O-waiting loads, with -ThrottleLimit as the brake.Object processing is half the story — objects have to come from somewhere. In episode 9 we learn working with files & folders: reading, writing, copying, moving, and exploring the filesystem as the biggest data source for your scripts. See you in episode 9!