PowerShell Pipeline
The pipeline is one of the most powerful and distinctive features of PowerShell. It connects commands together so the output of one command flows directly into the input of the next — like an assembly line where each station receives the previous station's product, works on it, and passes it forward.
What Is the Pipeline?
The pipeline symbol is the vertical bar |. Placing it between two commands tells PowerShell: "Take the output of the left command and pass it as input to the right command."
Command A | Command B | Command C
| | |
Produces Receives A's Receives B's
Objects output, output,
filters it formats it
# Without pipeline – two steps
$processes = Get-Process
$sorted = $processes | Sort-Object CPU -Descending
# With pipeline – one line
Get-Process | Sort-Object CPU -Descending
How the Pipeline Passes Objects
Unlike Linux pipelines which pass text, PowerShell passes full objects. Each object has properties and methods. Downstream commands can access those properties directly — no text parsing required.
# Get-Process returns Process objects
# Sort-Object receives those objects and sorts by the CPU property
# Select-Object picks only Name and CPU properties
# The result is a clean, formatted list
Get-Process | Sort-Object CPU -Descending | Select-Object Name, CPU -First 5
Output:
Name CPU
---- ---
chrome 18.45
code 12.30
explorer 5.10
svchost 2.80
notepad 0.50
Basic Pipeline Examples
Filter Running Services
Get-Service | Where-Object { $_.Status -eq "Running" }
Count Results
# Count how many services are running
Get-Service | Where-Object { $_.Status -eq "Running" } | Measure-Object
Sort and Display
# List all .exe files sorted by size
Get-ChildItem -Path "C:\Windows\System32" -Filter "*.exe" |
Sort-Object Length -Descending |
Select-Object Name, Length -First 10
Pipeline Variable – $_ (Dollar Underscore)
Inside Where-Object, ForEach-Object, and other pipeline cmdlets, $_ represents the current object passing through the pipeline.
$numbers = @(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
# Filter: keep only even numbers
$numbers | Where-Object { $_ % 2 -eq 0 }
# Output: 2 4 6 8 10
# Transform: multiply each number by 3
$numbers | ForEach-Object { $_ * 3 }
# Output: 3 6 9 12 15 18 21 24 27 30
Where-Object – Filter Pipeline Output
Where-Object (alias: ?) filters a collection and keeps only objects that match a condition.
# Files larger than 1 MB
Get-ChildItem -Path "C:\Windows" -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.Length -gt 1MB }
# Processes using more than 100 MB RAM
Get-Process | Where-Object { $_.WorkingSet -gt 100MB }
# Services with "SQL" in the display name
Get-Service | Where-Object { $_.DisplayName -like "*SQL*" }
Select-Object – Choose Properties
Select-Object picks specific properties from objects or selects the first/last N items.
# Get only Name and Status from services
Get-Service | Select-Object Name, Status
# Get first 5 items
Get-Process | Select-Object -First 5
# Get last 3 items
Get-Process | Select-Object -Last 3
# Get unique values
@("A", "B", "A", "C", "B") | Select-Object -Unique
# Output: A B C
Sort-Object – Sort Pipeline Output
# Sort processes by name (alphabetical)
Get-Process | Sort-Object Name
# Sort by CPU descending
Get-Process | Sort-Object CPU -Descending
# Sort files by size descending, then by name ascending
Get-ChildItem | Sort-Object Length -Descending, Name
# Sort strings
@("Banana", "Apple", "Mango", "Cherry") | Sort-Object
# Output: Apple Banana Cherry Mango
ForEach-Object – Transform Each Item
# Print each filename in uppercase
Get-ChildItem -Path "C:\Scripts" | ForEach-Object {
Write-Host $_.Name.ToUpper()
}
# Calculate size of each file in KB
Get-ChildItem -Path "C:\Logs" | ForEach-Object {
$sizeKB = [math]::Round($_.Length / 1KB, 2)
Write-Host "$($_.Name): $sizeKB KB"
}
Group-Object – Group Items by Property
# Group services by status
Get-Service | Group-Object Status
# Group files by extension
Get-ChildItem -Path "C:\Data" | Group-Object Extension
Output:
Count Name Group
----- ---- -----
87 Running {AdobeARMservice, AeLookupSvc ...}
52 Stopped {ALG, AppIDSvc ...}
Measure-Object – Calculate Statistics
# Count items
Get-Process | Measure-Object
# Sum file sizes
Get-ChildItem -Path "C:\Logs" | Measure-Object Length -Sum
# Min, Max, Average
@(10, 25, 40, 55, 70) | Measure-Object -Minimum -Maximum -Average -Sum
Output:
Count : 5
Average : 40
Sum : 200
Maximum : 70
Minimum : 10
Tee-Object – Split Pipeline to File and Screen
Tee-Object passes the pipeline output to both the screen and a file simultaneously — like a T-junction in plumbing.
# Display output on screen AND save to a file
Get-Process | Tee-Object -FilePath "C:\Logs\processes.txt" | Select-Object Name, CPU
Out-File and Out-GridView
# Save pipeline output to a text file
Get-Service | Out-File -FilePath "C:\Reports\services.txt"
# Display pipeline output in a visual grid (Windows only)
Get-Process | Out-GridView
Multi-Line Pipeline (Readability)
Long pipelines are easier to read when split across multiple lines. Place the pipe | at the end of each line.
Get-ChildItem -Path "C:\Windows\System32" -Filter "*.dll" |
Where-Object { $_.Length -gt 500KB } |
Sort-Object Length -Descending |
Select-Object Name, @{Name="SizeMB"; Expression={[math]::Round($_.Length/1MB, 2)}} |
Select-Object -First 10
Calculated Properties with Select-Object
A calculated property creates a new custom column in the output by defining a name and an expression.
Get-ChildItem -Path "C:\Logs" |
Select-Object Name,
@{Name="SizeKB"; Expression={[math]::Round($_.Length / 1KB, 2)}},
@{Name="LastModified"; Expression={$_.LastWriteTime.ToString("yyyy-MM-dd")}}
Output:
Name SizeKB LastModified
---- ------ ------------
app.log 128.45 2026-03-18
error.log 45.20 2026-03-20
debug.log 512.00 2026-03-21
Pipeline with Export-Csv
# Export running services to a CSV file
Get-Service |
Where-Object { $_.Status -eq "Running" } |
Select-Object Name, DisplayName, Status |
Export-Csv -Path "C:\Reports\running_services.csv" -NoTypeInformation
Write-Host "Report exported."
Pipeline Performance Tips
| Tip | Why It Matters |
|---|---|
Filter early with Where-Object | Reduces objects flowing through later stages |
| Use cmdlet-native parameters over pipeline filters | -Filter on Get-ChildItem is faster than piping to Where-Object |
Use Select-Object -First N early | Stops processing once N items are collected |
| Avoid storing large results in variables | Streaming through pipeline uses less memory |
Summary
The PowerShell pipeline connects commands into efficient processing chains. Objects flow from one cmdlet to the next, retaining all their properties throughout. Where-Object filters, Select-Object selects, Sort-Object sorts, ForEach-Object transforms, Measure-Object calculates, and Group-Object organizes. Calculated properties add custom columns. Multi-line formatting keeps complex pipelines readable. The pipeline is the most distinctive PowerShell skill — mastering it separates basic users from efficient scripters.
