PowerShell Select-Object: Choose and Shape Output

PowerShell cmdlets often return objects with dozens of properties when you only care about three. Displaying a Get-Process result that includes Handles, NPM, PM, WS, CPU, Id, SI, and Name is noisy — you want just Name, CPU, and memory. That’s exactly what PowerShell Select-Object does: it shapes pipeline output by choosing specific properties, computing new ones, limiting result counts, and deduplicating values. This guide covers every practical use.
Quick Answer / TL;DR
# Show only Name and CPU from Get-Process
Get-Process | Select-Object Name, CPU
# Get the top 5 by CPU
Get-Process | Sort-Object CPU -Descending | Select-Object -First 5 Name, CPU
Selecting Specific Properties
Pass property names to Select-Object to output only those columns:
# Select a few properties from Get-Process
Get-Process | Select-Object Name, Id, CPU, WorkingSet | Sort-Object CPU -Descending
# Select properties from Get-ChildItem
Get-ChildItem C:\Users\Public\Documents |
Select-Object Name, Length, LastWriteTime
# Select from Get-Service
Get-Service | Select-Object Name, Status, StartType
Name Id CPU WorkingSet
---- -- --- ----------
chrome 4892 1245.3 548732928
vscode 7123 187.2 165429248
pwsh 3456 12.1 87654321
The output object has only the selected properties — the others are discarded. This makes downstream processing faster because fewer properties are carried through the pipeline.
Using -First and -Last to Limit Output
-First N keeps only the first N objects; -Last N keeps only the last N:
# Top 10 memory consumers
Get-Process | Sort-Object WorkingSet -Descending | Select-Object -First 10
# Last 5 files created (newest)
Get-ChildItem C:\Logs | Sort-Object LastWriteTime | Select-Object -Last 5
# Skip first 10, take next 5 (pagination)
Get-ChildItem C:\Logs | Select-Object -Skip 10 -First 5
Name WorkingSet
---- ----------
chrome 548732928
outlook 189267968
vscode 165429248
...
Calculated Properties with @{Name=;Expression=}
Add custom computed properties using a hashtable syntax — this is one of the most useful features:
# Add a size in MB column
Get-ChildItem C:\Logs |
Select-Object Name,
@{ Name='SizeMB'; Expression={ [math]::Round($_.Length / 1MB, 2) } },
LastWriteTime
# Add a percentage column for processes
Get-Process | Sort-Object WorkingSet -Descending | Select-Object -First 5 Name,
@{ Name='MemMB'; Expression={ [math]::Round($_.WorkingSet / 1MB, 1) } },
@{ Name='CPURounded'; Expression={ [math]::Round($_.CPU, 1) } }
Name SizeMB LastWriteTime
---- ------ -------------
app.log 2.45 5/4/2026 9:15:22 AM
error.log 0.87 5/4/2026 8:01:05 AM
debug.log 5.12 5/3/2026 11:45:00 PM
The hashtable format is @{ Name='ColumnHeader'; Expression={ script_block } }. Inside the expression, $_ is the current object. You can use N and E as shorthand aliases for Name and Expression.
-ExpandProperty to Unwrap Arrays
-ExpandProperty extracts a single property’s value directly, without creating a wrapper object:
# Regular -Property returns an object with a Name property
Get-Process | Select-Object -Property Name | Get-Member # Object with .Name
# -ExpandProperty returns the raw string values
$names = Get-Process | Select-Object -ExpandProperty Name
$names[0] # 'AggregatorHost' — just a string, not a wrapper object
# Practical use: get an array of values for later use
$runningServices = Get-Service |
Where-Object { $_.Status -eq 'Running' } |
Select-Object -ExpandProperty Name
$runningServices -join ', '
AggregatorHost
AppInfo, Audiosrv, BFE, BrokerInfrastructure, CDPSvc...
Use -ExpandProperty when you need a flat array of values (like a list of names or IDs) rather than an array of objects. This is essential before using the values as parameters, in SQL queries, or joining into a string.
-Unique for Deduplication
# Get unique file extensions in a folder
Get-ChildItem C:\Users\Public\Documents |
Select-Object -ExpandProperty Extension |
Sort-Object -Unique
# Deduplicate objects by a property
Get-EventLog -LogName System -Newest 1000 |
Select-Object Source -Unique |
Sort-Object Source
.docx
.pdf
.txt
.xlsx
Select-Object vs Format-Table
A common confusion: both can limit visible columns, but they work very differently:
- Select-Object — creates new objects with only the chosen properties. Output is still objects, can be piped further, exported to CSV, etc.
- Format-Table — formats objects as a table for display. Output is formatting instructions, not objects. Do not pipe
Format-Tableoutput to other cmdlets that expect objects.
# This works — Select-Object output can be exported
Get-Process | Select-Object Name, CPU | Export-Csv 'C:\Reports\procs.csv' -NoTypeInformation
# This FAILS — Format-Table output cannot be exported as data
Get-Process | Format-Table Name, CPU | Export-Csv 'C:\Reports\procs.csv' # Wrong!
Common Errors and Fixes
-
-ExpandProperty vs -Property returning different types:
Select-Object -Property Namereturns an object with aNameproperty.Select-Object -ExpandProperty Namereturns the string itself. If downstream code expects a string but gets an object, switch to-ExpandProperty. -
Calculated property syntax — easy to get the hashtable wrong: The correct form is
@{ Name='Label'; Expression={ $_.Property } }. Common mistakes: forgetting theNamekey, using=instead of the key-value format, or forgetting the script block braces around the expression.
Related Cmdlets / See Also
Wrapping Up
Select-Object is the tool for shaping pipeline data: pick only the properties you need, add calculated columns, limit result count, and deduplicate with -Unique. Use -ExpandProperty when you need flat values rather than wrapper objects. Keep objects as objects — don’t use Format-Table until the very end. Your next step: build a process report with calculated columns for memory in MB and export it to CSV.


