Skip to content

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:

Process chrome is using 120.5 seconds of CPU
Process powershell is using 5.2 seconds of CPU

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

  1. Get-Process - Creates process objects
  2. Where-Object - Receives full process objects, checks CPU property, passes matching objects
  3. Sort-Object - Receives filtered objects, sorts by CPU property, passes sorted objects
  4. Select-Object - Receives sorted objects, picks first 5, passes them
  5. 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

# Don't know what properties are available? Use Get-Member!
Get-Process | Get-Member
Get-Service | Get-Member
Get-ChildItem | Get-Member

# Filter to just properties
Get-Process | Get-Member -MemberType Property

# Filter to just methods
Get-Process | Get-Member -MemberType Method

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

# $_ (or $PSItem) represents the current object in the pipeline
Get-Process | Where-Object {$_.CPU -gt 50}
#                            ^^^^ current process object

Get-ChildItem | ForEach-Object {
Write-Output "$($_.Name) is $($_.Length) bytes"
#              ^^^^ current file object
}

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

# Default display shows only some properties
Get-Process
# Shows: Name, CPU, WS, etc.

# But there are MANY more properties available!
Get-Process | Select-Object *
# Shows: ALL properties including hidden ones

# Use Get-Member to see everything
Get-Process | Get-Member -MemberType Property

Common Mistakes

Treating Objects Like Text

# BAD - Trying to parse text that doesn't exist
Get-Process | Select-String "chrome"  # ERROR! These are objects, not text

# GOOD - Filter by property
Get-Process | Where-Object {$_.Name -like "*chrome*"}

Forgetting Properties Are Typed

# BAD - Comparing string to number
Get-ChildItem | Where-Object {$_.Length -eq "1000"}  # Might not work as expected

# GOOD - Use proper types
Get-ChildItem | Where-Object {$_.Length -eq 1000}  # 1000 is a number
Get-ChildItem | Where-Object {$_.Length -gt 1MB}   # Use PowerShell size literals

Additional Resources