The Pipeline

Master PowerShell's pipeline to chain commands together, filter data, sort results, and transform output. Learn how object-based piping works.

📖 5 min read📅 2026-02-10Core Concepts

What is the Pipeline?

The pipeline is PowerShell's most powerful feature. It lets you chain commands together using the pipe symbol (|), where the output of one command becomes the input of the next.

# Basic pipeline: Get processes, then sort by CPU usage
Get-Process | Sort-Object CPU -Descending

Unlike Bash (which pipes text), PowerShell pipes objects. This means you have access to structured properties and methods at every stage.

How the Pipeline Works

# Step by step:
# 1. Get-Process returns process OBJECTS
# 2. Objects flow through the pipe |
# 3. Sort-Object receives those objects and sorts them
# 4. Select-Object picks specific properties to display
 
Get-Process | Sort-Object CPU -Descending | Select-Object -First 10 Name, CPU, WorkingSet

Think of it as an assembly line:

[Get Data] → | → [Filter] → | → [Sort] → | → [Format] → | → [Output]

Essential Pipeline Cmdlets

Where-Object — Filtering

Filter objects based on conditions:

# Get processes using more than 100MB of memory
Get-Process | Where-Object { $_.WorkingSet -gt 100MB }
 
# Get running services
Get-Service | Where-Object { $_.Status -eq "Running" }
 
# Get large files
Get-ChildItem -Recurse | Where-Object { $_.Length -gt 1MB }
 
# Simplified syntax (PowerShell 3+)
Get-Service | Where-Object Status -eq "Running"
 
# Multiple conditions
Get-Process | Where-Object { $_.CPU -gt 10 -and $_.WorkingSet -gt 50MB }

The $_ variable represents the current object in the pipeline (also available as $PSItem).

Select-Object — Choosing Properties

Select specific properties or a subset of results:

# Select specific properties
Get-Process | Select-Object Name, CPU, WorkingSet, Id
 
# Select first/last N items
Get-Process | Sort-Object CPU -Descending | Select-Object -First 5
Get-Process | Sort-Object CPU -Descending | Select-Object -Last 3
 
# Create calculated properties
Get-Process | Select-Object Name, @{
    Name = "MemoryMB"
    Expression = { [math]::Round($_.WorkingSet / 1MB, 2) }
}
 
# Select unique values
Get-Process | Select-Object Name -Unique
 
# Expand a single property to get raw values
Get-Process | Select-Object -ExpandProperty Name

Sort-Object — Sorting

# Sort ascending (default)
Get-Process | Sort-Object Name
 
# Sort descending
Get-Process | Sort-Object CPU -Descending
 
# Sort by multiple properties
Get-ChildItem | Sort-Object Extension, Name
 
# Sort unique values
1, 3, 2, 3, 1, 5 | Sort-Object -Unique

ForEach-Object — Iterating

Execute an action on each object:

# Simple iteration
1..10 | ForEach-Object { $_ * 2 }
 
# Process each file
Get-ChildItem *.txt | ForEach-Object {
    Write-Host "Processing: $($_.Name)"
    Get-Content $_.FullName | Measure-Object -Line
}
 
# Simplified syntax
Get-Process | ForEach-Object Name
 
# Using method syntax
"hello", "world" | ForEach-Object ToUpper

Group-Object — Grouping

# Group processes by name
Get-Process | Group-Object Name | Sort-Object Count -Descending
 
# Group files by extension
Get-ChildItem -Recurse | Group-Object Extension | Sort-Object Count -Descending
 
# Group services by status
Get-Service | Group-Object Status

Measure-Object — Statistics

# Count items
Get-Process | Measure-Object
 
# Get statistics on a property
Get-Process | Measure-Object CPU -Sum -Average -Maximum -Minimum
 
# Count lines, words, characters in a file
Get-Content "file.txt" | Measure-Object -Line -Word -Character

Pipeline Output

Format-Table and Format-List

# Table format (default for many commands)
Get-Process | Format-Table Name, CPU, WorkingSet -AutoSize
 
# List format (shows all properties vertically)
Get-Process -Name "pwsh" | Format-List *
 
# Custom column widths
Get-Service | Format-Table @{
    Label = "Service Name"
    Expression = { $_.Name }
    Width = 30
}, Status -AutoSize

Exporting Data

# Export to CSV
Get-Process | Select-Object Name, CPU, WorkingSet |
    Export-Csv -Path "processes.csv" -NoTypeInformation
 
# Export to JSON
Get-Service | ConvertTo-Json | Out-File "services.json"
 
# Export to HTML
Get-Process | Select-Object Name, CPU, WorkingSet |
    ConvertTo-Html -Title "Process Report" |
    Out-File "report.html"
 
# Export to XML
Get-Process | Export-Clixml "processes.xml"

Redirecting Output

# Send to file (overwrite)
Get-Process | Out-File "processes.txt"
 
# Append to file
Get-Process | Out-File "processes.txt" -Append
 
# Send to clipboard
Get-Process | Select-Object Name | Set-Clipboard
 
# Suppress output
Get-Process | Out-Null
 
# Display in grid view (Windows only)
Get-Process | Out-GridView

Advanced Pipeline Techniques

Tee-Object — Split the Pipeline

Send output to both a file and the next command:

Get-Process | Tee-Object -FilePath "all-processes.txt" |
    Where-Object CPU -gt 10 |
    Select-Object Name, CPU

Compare-Object — Comparing

# Compare two arrays
$before = Get-Process
# ... do something ...
$after = Get-Process
Compare-Object $before $after -Property Name

Pipeline with Where and ForEach

# Real-world example: Find large log files and get their sizes
Get-ChildItem -Path "C:\Logs" -Filter "*.log" -Recurse |
    Where-Object { $_.Length -gt 10MB } |
    Sort-Object Length -Descending |
    ForEach-Object {
        [PSCustomObject]@{
            Name     = $_.Name
            SizeMB   = [math]::Round($_.Length / 1MB, 2)
            Modified = $_.LastWriteTime
            Path     = $_.DirectoryName
        }
    } |
    Format-Table -AutoSize

Pipeline Performance Tips

# GOOD: Filter early (left-side filtering)
Get-ChildItem -Path "C:\" -Filter "*.log" -Recurse
 
# BAD: Filter late (processes all files first)
Get-ChildItem -Path "C:\" -Recurse | Where-Object Extension -eq ".log"
 
# GOOD: Use cmdlet parameters instead of Where-Object when possible
Get-Process -Name "chrome"
 
# BAD: Gets all processes then filters
Get-Process | Where-Object Name -eq "chrome"

Rule of thumb: Filter as far LEFT (early) in the pipeline as possible. Use cmdlet parameters for filtering before resorting to Where-Object.

Exercises

  1. List the 10 processes consuming the most memory, showing only Name and MemoryMB
  2. Find all .txt files in your Documents folder and count them
  3. Group running services by their StartType and count each group
  4. Export a list of all environment variables to a CSV file
  5. Create a pipeline that finds files modified in the last 24 hours

Next up: Variables and Data Types — learn how to store and work with data in PowerShell!