What truly sets PowerShell apart from traditional command-line shells like Bash or Zsh is its foundational design: it’s built around objects, not text. While other shells pass streams of text between commands, PowerShell passes rich, structured objects. This fundamental difference is PowerShell’s superpower, making it an incredibly efficient and robust tool for automation, data processing, and system administration.

This guide explores what it means to work with objects and how I leverage the object-oriented pipeline to write cleaner, more powerful, and more reliable scripts.


From Text Streams to Object Pipelines: The Big Shift

In a traditional shell, if I want to get a list of running processes, I might run ps aux. This command outputs a block of plain text. To get specific information, like the process ID of a single process, I have to manually parse that text with tools like grep, awk, or sed. This approach is fragile; if the output format of ps aux changes in a future OS update, my script will break.

PowerShell takes a different approach. When I run Get-Process, it doesn’t return text. It returns a collection of **.NET objects** (specifically, [System.Diagnostics.Process]objects). Each object is a structured bundle of information containing **properties** (likeId, ProcessName, and CPU`) and methods (actions the object can perform).

Bash (Text-Based):

# Returns plain text that needs to be parsed
ps aux | grep 'chrome'

PowerShell (Object-Based):

# Returns a collection of process objects
Get-Process -Name 'chrome'

Because I’m working with objects, I never need to parse text. I can directly access the properties I need.


Meet My Best Friends: The Core Object Cmdlets

PowerShell’s real power comes from a set of core cmdlets designed to manipulate objects in the pipeline.

Get-Member: Discovering an Object’s Properties and Methods

Get-Member is my best friend for exploring objects. It tells me everything I need to know about an object, including its type, properties, and methods.

Get-Process -Name "powershell" | Get-Member

Select-Object: Picking the Properties I Need

Instead of parsing text columns, I can use Select-Object to pick the exact properties I want.

Get-Process | Select-Object -Property ProcessName, Id, WorkingSet

Where-Object: Filtering Objects Based on Their Properties

Where-Object allows me to filter a collection of objects based on the values of their properties.

# Find all services that are currently running
Get-Service | Where-Object -Property Status -EQ 'Running'

Sort-Object: Sorting Objects by Their Properties

I can sort any collection of objects using one or more of their properties.

# Find the top 5 most CPU-intensive processes
Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 5

Real-World Scenario: Managing Windows Services

I’ll show how these cmdlets work together in a practical scenario. I imagined I wanted to find all stopped services that are set to start automatically and then start them.

Step 1: Get all services.

Get-Service

Step 2: Filter for the services I want. I only want services that are Stopped and have a StartType of Automatic.

Get-Service | Where-Object { ($_.Status -eq 'Stopped') -and ($_.StartType -eq 'Automatic') }

Step 3: Perform an action on each service. Now, I can pipe these filtered objects to the Start-Service cmdlet.

Get-Service | Where-Object { ($_.Status -eq 'Stopped') -and ($_.StartType -eq 'Automatic') } | Start-Service -Verbose

This is the magic of the object pipeline. The Start-Service cmdlet understands the service objects passed to it and acts on them. No text parsing, no loops, just a clean, readable one-liner.


Creating My Own Objects with [PSCustomObject]

I’m not limited to the objects provided by cmdlets. I can easily create my own structured data using [PSCustomObject]. This is incredibly useful for creating custom reports or structured data to pass to other commands.

$server = [PSCustomObject]@{
    Name   = "WebServer01"
    IP     = "192.168.10.15"
    Status = "Online"
    OS     = "Windows Server 2022"
}

# The custom object can be used just like any other object
Write-Host "Pinging server $($server.Name) at IP $($server.IP)..."

The Universal Translators: ConvertTo-Json and ConvertTo-Csv

Because PowerShell works with structured objects, it can seamlessly convert that data to and from other structured formats like JSON and CSV.

# Get the top 5 processes and export them to a CSV file
Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 5 |
    Export-Csv -Path "C:\Reports\TopProcesses.csv" -NoTypeInformation

# Get a service object and convert it to JSON
Get-Service -Name "WinRM" | ConvertTo-Json

Conclusion

Mastering PowerShell’s object-oriented nature was the key for me to unlock its full potential. By thinking in terms of objects instead of text, I can write scripts that are:

  • More Robust: My scripts won’t break if the text format of a command’s output changes.
  • More Readable: Accessing properties by name ($process.CPU) is much clearer than parsing the fifth column of a text block.
  • More Powerful: I can easily filter, sort, group, and manipulate complex data with a few simple commands.

The next time I’m writing a script, I use Get-Member to explore the objects I’m working with. I embrace the pipeline, and I soon find myself writing automation that is more efficient and reliable than ever before.