What is the Pipeline?
The pipeline is PowerShell's most powerful feature. It lets you chain commands together so the output of one command becomes the input of the next — using the pipe character |.
Instead of processing text like traditional shells, PowerShell passes objects through the pipeline. This means you can access properties and methods at every step.
$processes = Get-Process
$sorted = $processes | Sort-Object CPU -Descending
Get-Process | Sort-Object CPU -Descending | Select-Object -First 5
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
112 245.2 312.5 8823.4 1234 1 chrome
56 88.1 104.2 1240.1 5678 1 Code
...
💡
Reading a pipeline: Read it left to right. "Get all processes, then sort them by CPU descending, then take only the first 5." Each | means "and then send that to..."
Where-Object — Filtering
Where-Object (alias: ? or where) filters a collection to only include objects that match a condition. Think of it as a "keep only if" filter.
Where-Object — Two Syntaxes
Get-Service | Where-Object Status -eq 'Running'
Get-Service | Where-Object { $_.Status -eq 'Running' }
Get-Process | Where-Object {
$_.CPU -gt 10 -and $_.WorkingSet -gt 50MB
}
Comparison Operators
PowerShell uses letter-based operators instead of symbols (this avoids confusion with XML/HTML):
| Operator | Meaning | Example |
| -eq | Equal to | $x -eq 5 |
| -ne | Not equal to | $x -ne 5 |
| -gt | Greater than | $x -gt 10 |
| -ge | Greater than or equal | $x -ge 10 |
| -lt | Less than | $x -lt 10 |
| -le | Less than or equal | $x -le 10 |
| -like | Wildcard match (* and ?) | $name -like "A*" |
| -notlike | Wildcard no match | $name -notlike "B*" |
| -match | Regex match | $str -match "^\d+" |
| -contains | Array contains value | $arr -contains "x" |
| -in | Value is in array | "x" -in $arr |
| -not / ! | Logical NOT | -not $active |
| -and | Logical AND | $a -gt 5 -and $b -lt 10 |
| -or | Logical OR | $a -eq 1 -or $a -eq 2 |
✅
Case sensitivity: By default all comparison operators are case-insensitive. For case-sensitive matching, add 'c' prefix: -ceq, -clike, -cmatch.
Select-Object — Choosing Properties
Select-Object lets you pick which properties to keep, limit how many objects you get, or create calculated (custom) properties:
Get-Process | Select-Object Name, Id, CPU
Get-ChildItem | Select-Object -First 5
Get-ChildItem | Select-Object -Last 3
Get-Process | Select-Object -Skip 10 -First 5
Get-Process | Select-Object -ExpandProperty Company -Unique
Get-Process | Select-Object Name, @{
Name = 'Memory (MB)'
Expression = { [math]::Round($_.WorkingSet / 1MB, 1) }
} | Sort-Object 'Memory (MB)' -Descending
Sort-Object — Sorting Results
Get-Service | Sort-Object Name
Get-Process | Sort-Object CPU -Descending
Get-Service | Sort-Object Status, Name
Get-Process | Sort-Object WorkingSet -Descending | Select-Object -First 10 | Format-Table Name, Id
ForEach-Object — Processing Each Item
ForEach-Object (alias %) runs a script block for each object in the pipeline. Inside the block, $_ is the current object:
1..5 | ForEach-Object { $_ * 2 }
2 4 6 8 10
Get-Service | Where-Object Status -eq Running | ForEach-Object {
Write-Host "Service: $($_.DisplayName)" -ForegroundColor Green
}
Get-ChildItem *.txt | ForEach-Object {
Write-Host "$($_.Name) — $($_.Length) bytes"
}
Group-Object and Measure-Object
Get-Service | Group-Object Status
Count Name Group
----- ---- -----
178 Stopped {AarSvc, AJRouter...}
92 Running {AdobeARMservice, Appinfo...}
Get-Process | Measure-Object WorkingSet -Sum -Average -Maximum
Count : 180
Average : 35821234
Sum : 6447822120
Maximum : 652812288
(Get-Content myfile.txt | Measure-Object -Line).Lines
Putting It All Together
Here are some real-world one-liners that combine multiple pipeline stages:
Real-World Pipeline Examples
Get-ChildItem C:\Windows -File -Recurse -ErrorAction SilentlyContinue
| Sort-Object Length -Descending
| Select-Object Name, @{N='Size(MB)';E={[math]::Round($_.Length/1MB,2)}}
| Select-Object -First 5
| Format-Table -AutoSize
Get-Service | Where-Object Status -eq Running | Sort-Object Name | Select-Object Name, DisplayName
Get-ChildItem C:\Logs -Recurse -File
| Measure-Object Length -Sum
| Select-Object @{N='TotalMB';E={[math]::Round($_.Sum/1MB,2)}}
🧪 Try It Yourself
- Run
Get-Process | Sort-Object CPU -Descending | Select-Object -First 5 | Format-Table Name, Id, CPU -AutoSize
- List all Running services:
Get-Service | Where-Object Status -eq 'Running'
- Count files by extension:
Get-ChildItem C:\Windows -File | Group-Object Extension | Sort-Object Count -Descending | Select-Object -First 5
- Build your own pipeline — get processes, filter ones using more than 50MB RAM, sort by memory, show Name and memory as MB
Key Takeaways
- The pipeline (
|) passes objects from one command to the next
Where-Object filters the collection — use $_ for the current object
- Comparison operators:
-eq -ne -gt -lt -like -match -contains
Select-Object picks properties, limits count, creates custom properties
Sort-Object orders results; add -Descending to reverse
ForEach-Object runs code for each item; $_ is the current item
- Combine pipeline stages to build powerful one-liner data transformations