Understanding Objects & The Pipeline
Note
Learn what PowerShell objects are, how they differ from text, and why they make PowerShell's pipeline so powerful.
Overview
PowerShell is fundamentally different from traditional command-line shells because it works with objects instead of text. Understanding this concept is the key to unlocking PowerShell's true power.
Think of an object as a structured container that holds related information (properties) and actions (methods). Instead of getting raw text that you have to parse, PowerShell gives you organized data you can directly work with.
What is an Object?
An object is a structured piece of data that has:
- Properties - Information about the object (like attributes or characteristics)
- Methods - Actions you can perform on the object (like functions)
- Type - What kind of object it is
Real-World Analogy
Think of a car as an object:
- Properties: Color (red), Make (Toyota), Model (Camry), Year (2023), Miles (15,000)
- Methods: Start(), Stop(), Accelerate(), Turn()
- Type: Car
In PowerShell, when you run Get-Process, each process is an object with properties like Name, CPU usage, Memory, and methods like Kill(), WaitForExit(), etc.
Objects vs Text
Traditional Shell (Text-Based)
# In Bash/CMD, you get text output
ps aux
# Output (text):
# USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# root 1 0.0 0.1 16844 3520 ? Ss Nov29 0:01 /sbin/init
# To get just the PID, you have to parse text:
ps aux | grep myapp | awk '{print $2}'
Problems with text:
- Have to figure out where columns start/end
- Numbers are strings, not numbers
- No way to know what type of data each column contains
- Fragile - breaks if spacing changes
PowerShell (Object-Based)
# In PowerShell, you get objects
Get-Process
# Each process is an object with properties you can access directly
# To get just the process ID - access the property!
$process = Get-Process -Name "powershell" | Select-Object -First 1
$process.Id # Access the Id property directly
$process.Name # Access the Name property
$process.CPU # Access CPU time as a real number
Benefits of objects:
- Properties are already separated - no parsing needed
- Types are preserved - numbers are numbers, dates are dates
- IntelliSense works - your editor can suggest properties
- Robust - structure doesn't break with formatting changes
Exploring Objects
See All Properties with Get-Member
# Get information about what an object contains
Get-Process | Get-Member
# Output shows:
# TypeName: System.Diagnostics.Process
#
# Name MemberType Definition
# ---- ---------- ----------
# Handles Property int Handles {get;}
# Id Property int Id {get;}
# ProcessName Property string ProcessName {get;}
# CPU Property double CPU {get;}
# Kill Method void Kill()
# WaitForExit Method void WaitForExit()
Accessing Properties
# Get a single process
$process = Get-Process -Name "powershell" | Select-Object -First 1
# Access properties using dot notation
$process.ProcessName # "powershell"
$process.Id # 1234
$process.CPU # 12.5 (a real number!)
$process.StartTime # 11/29/2025 9:00:00 AM (a real DateTime!)
# Properties are strongly typed
$process.CPU.GetType() # System.Double
$process.Id.GetType() # System.Int32
Accessing Methods
# Methods are actions you can perform
$process = Get-Process -Name "notepad" | Select-Object -First 1
# Call a method with ()
$process.Kill() # Stops the process
$process.WaitForExit() # Waits for process to exit
$process.Refresh() # Updates the process information
Common Use Cases
Use Case 1: Getting Specific Properties
What it does: Extract just the data you need from objects
# Get only Name and CPU from processes
Get-Process | Select-Object -Property Name, CPU
# Get only Name and Length from files
Get-ChildItem -Path C:\Temp | Select-Object -Property Name, Length
# Access properties directly in a loop
Get-Process | ForEach-Object {
Write-Output "Process $($_.Name) is using $($_.CPU) seconds of CPU"
}
Output:
Use Case 2: Filtering by Properties
What it does: Filter objects based on their property values
# Get processes using more than 100 seconds of CPU
Get-Process | Where-Object {$_.CPU -gt 100}
# Get files larger than 10MB
Get-ChildItem -Path C:\Temp | Where-Object {$_.Length -gt 10MB}
# Get services that are stopped
Get-Service | Where-Object {$_.Status -eq "Stopped"}
Use Case 3: Sorting by Properties
What it does: Order objects by their property values
# Sort processes by CPU usage (highest first)
Get-Process | Sort-Object -Property CPU -Descending
# Sort files by size
Get-ChildItem | Sort-Object -Property Length
# Sort by multiple properties
Get-Process | Sort-Object -Property CPU, WS -Descending
How Objects Flow Through the Pipeline
The pipeline (|) passes entire objects from one command to the next - not text!
# Each command receives the full object from the previous command
Get-Process | # Outputs process objects
Where-Object {$_.CPU -gt 50} | # Receives process objects, filters them
Sort-Object -Property CPU | # Receives filtered process objects, sorts them
Select-Object -First 5 | # Receives sorted process objects, picks top 5
Format-Table Name, CPU, WS # Receives 5 process objects, formats for display
What Happens at Each Step
- Get-Process - Creates process objects
- Where-Object - Receives full process objects, checks CPU property, passes matching objects
- Sort-Object - Receives filtered objects, sorts by CPU property, passes sorted objects
- Select-Object - Receives sorted objects, picks first 5, passes them
- Format-Table - Receives 5 objects, displays Name, CPU, WS properties
Key point: At every step, you have access to ALL properties of the object, not just what's visible!
Real-World Examples
Example: Finding Large Files and Calculating Total Size
Scenario: Find all files over 100MB and calculate total space used
# Get large files
$largeFiles = Get-ChildItem -Path C:\Data -Recurse |
Where-Object {$_.Length -gt 100MB}
# Because Length is a real number (not text), we can do math!
$totalSizeGB = ($largeFiles | Measure-Object -Property Length -Sum).Sum / 1GB
# Access properties directly
Write-Output "Found $($largeFiles.Count) large files"
Write-Output "Total size: $([math]::Round($totalSizeGB, 2)) GB"
# Display file details
$largeFiles | Select-Object Name, @{Name="SizeMB";Expression={[math]::Round($_.Length/1MB, 2)}}
Explanation: Because Length is a numeric property (not text), we can directly use it in calculations, pass it to Measure-Object, and perform division. No text parsing required!
Example: Working with DateTime Properties
Scenario: Find files modified in the last 24 hours
# LastWriteTime is a DateTime object, not text!
$cutoffTime = (Get-Date).AddHours(-24)
Get-ChildItem -Path C:\Logs |
Where-Object {$_.LastWriteTime -gt $cutoffTime} |
Select-Object Name, LastWriteTime
Explanation: Because LastWriteTime is a DateTime object, we can directly compare it to another DateTime. PowerShell knows how to compare dates - we don't have to parse text dates!
Example: Using Methods on Objects
Scenario: Restart all stopped services that should be running
# Get stopped services that are set to Automatic
$stoppedServices = Get-Service |
Where-Object {($_.Status -eq "Stopped") -and ($_.StartType -eq "Automatic")}
# Use the Start() method on each service object
foreach ($service in $stoppedServices) {
Write-Output "Starting $($service.Name)..."
$service.Start() # Calling a method on the object!
$service.WaitForStatus('Running', '00:00:30') # Wait up to 30 seconds
}
Explanation: Service objects have methods like Start(), Stop(), WaitForStatus() that we can call directly. The object knows how to perform these actions!
Creating Custom Objects
You can create your own objects with custom properties:
# Create a custom object using PSCustomObject
$user = [PSCustomObject]@{
Name = "Raymond"
Department = "IT"
Email = "raymond@company.com"
StartDate = (Get-Date "2020-01-15")
}
# Access properties
$user.Name # "Raymond"
$user.Department # "IT"
# Add to an array
$users = @()
$users += [PSCustomObject]@{Name="John"; Department="HR"}
$users += [PSCustomObject]@{Name="Jane"; Department="IT"}
# Filter your custom objects
$users | Where-Object {$_.Department -eq "IT"}
Property Types Matter
Different property types enable different operations:
# String properties
$file = Get-Item "C:\Temp\file.txt"
$file.Name.ToUpper() # "FILE.TXT" - string method
$file.Name.Length # 8 - string property
# Numeric properties
$process = Get-Process | Select-Object -First 1
$process.CPU * 2 # Can do math!
$process.WS / 1MB # Convert bytes to megabytes
# DateTime properties
$file.LastWriteTime.AddDays(-7) # 7 days ago
$file.LastWriteTime.ToString("yyyy-MM-dd") # "2025-11-29"
# Boolean properties
$service = Get-Service -Name "Spooler"
if ($service.CanStop) {
$service.Stop() # Only stop if CanStop is true
}
Tips & Tricks
Use Get-Member to Explore
Select-Object vs Direct Property Access
# Get single property from multiple objects
Get-Process | Select-Object -ExpandProperty Name
# Returns: array of strings ["chrome", "powershell", ...]
# vs using Select-Object without -ExpandProperty
Get-Process | Select-Object Name
# Returns: array of objects with Name property
# Direct access for single object
$proc = Get-Process -Name "powershell" | Select-Object -First 1
$proc.Name # Direct property access
Pipeline Variables
Format Cmdlets Break Objects
# BAD - Format-Table converts objects to formatted text
$result = Get-Process | Format-Table
$result[0].Name # ERROR! $result is not objects anymore
# GOOD - Keep objects until the end
$processes = Get-Process | Where-Object {$_.CPU -gt 50}
$processes[0].Name # Works! Still objects
$processes | Format-Table # Format only when displaying
Not All Properties Are Visible by Default
Common Mistakes
Treating Objects Like Text
Forgetting Properties Are Typed
Related Topics
- The Pipeline - How objects flow through commands
- Object Manipulation - ForEach-Object, Where-Object, and more
- Variables & Data Types - Storing and working with objects
- Cmdlets Introduction - Commands that produce objects
- Formatting Output - Displaying objects effectively