Loosely Decoupled
GuideAutomation

What Does This PowerShell Script Do?

Read a script you copied with the tools built into PowerShell, without running anything that changes your files.

A tired man in rubber gloves and headphones opens a box holding a long paper scroll with tongs from behind a safety screen, while a cheerful tin robot gives a confident thumbs-up. A small tag on the scroll is cyan.
On this page

TL;DR

Take the script apart one command at a time, and ask PowerShell about each piece:

PowerShell
# What a name runs, and where it comes from.
Get-Command sort
# What a cmdlet is for, with worked examples (run Update-Help once first).
Get-Help Sort-Object -Examples
# What the objects in a pipeline carry.
Get-ChildItem | Get-Member
# What a command would change, without changing it.
Get-ChildItem -Recurse -Filter *.log | Remove-Item -WhatIf

Check Get-Command first: a short name such as sort can run a different program on macOS or Linux than it does on Windows.

A question on r/PowerShell (opens in a new tab) put it plainly: someone needed to export names from Exchange, found a script, pasted it in, and it worked. They had no idea what it did, and wondered whether they could say they knew PowerShell.

To me, knowing PowerShell means you can sit at a prompt with a repetitive task and find your way to automating it: the basics, plus knowing how to find the right cmdlets. Writing your own modules is where that leads, not the entry requirement. If you’re learning PowerShell, reading the scripts you already use this way is a faster start than memorizing cmdlets. And the way to find the right cmdlets is the same way to understand a script someone else wrote: ask PowerShell itself. Every step below is one of those questions.

The example is a one-liner of the sort people paste: list the three largest files under the current folder.

PowerShell
gci -r -File | sort Length -Desc | select -First 3 Name, @{n='MB';e={[math]::Round($_.Length/1MB,1)}}

Find out what each command is

A pipeline is commands joined by |. Before anything else, ask what each name runs. Get-Command resolves aliases and shows where a command comes from:

Command
PS> Get-Command gci, sort, select

Output


CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           gci -> Get-ChildItem
Application     sort                                               0.0.0.0    /usr/bin/sort
Alias           select -> Select-Object

gci and select are aliases for cmdlets. sort isn’t: on macOS, PowerShell has no sort alias, so the name runs the Unix sort program, which doesn’t understand -Desc. A one-liner written on Windows, where sort means Sort-Object, fails here. Replace each alias with the full name it stands for, and the script reads the same everywhere:

PowerShell
Get-ChildItem -Recurse -File |
    Sort-Object -Property Length -Descending |
    Select-Object -First 3 -Property Name, @{Name = 'MB'; Expression = { [math]::Round($_.Length / 1MB, 1) }}

Get-Alias -Definition Get-ChildItem works the other way, listing every short name for a cmdlet. The same command decodes the two names that look least like commands, ? and %:

Command
PS> Get-Alias -Definition Where-Object, ForEach-Object

Output


CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           ? -> Where-Object
Alias           where -> Where-Object
Alias           % -> ForEach-Object
Alias           foreach -> ForEach-Object

Inside their braces, $_ is the object the pipeline is handing over at that moment.

Preview what a command would change

Before running any part of the script, find the commands that change something. Most built-in cmdlets that delete, move or overwrite take -WhatIf, which reports what they would do instead of doing it:

Command
PS> Get-ChildItem -Recurse -File -Filter *.log | Remove-Item -WhatIf

Output

What if: Performing the operation "Remove File" on target "<folder>/logs/app.log".
What if: Performing the operation "Remove File" on target "<folder>/logs/old.log".

-WhatIf works only on commands that support it: cmdlets, and functions declared with SupportsShouldProcess. On macOS, rm, mv and cp aren’t aliases but the system programs, which don’t know -WhatIf at all, so a Windows script’s rm -WhatIf doesn’t preview anything; Get-Command shows which you have. A function can also accept -WhatIf and still change things outside its ShouldProcess check, so read its body too.

Read the help and its examples

Get-Help explains a cmdlet, and -Examples shows it in use. A fresh PowerShell installation has no local help files, so install them once:

PowerShell
Update-Help -Scope CurrentUser
Get-Help Sort-Object -Examples

Update-Help downloads the help for every installed module; add -Module with a module name to fetch only one.

Run the pipeline one stage at a time

Run only the stages that read. A cmdlet’s verb is a first clue, not a guarantee: Get and Select usually read, while Remove, Set, New, Move, Copy, Rename, Clear, Out, Export and Invoke change things and get -WhatIf first, as shown earlier. Read any { } block too: the code inside Where-Object or ForEach-Object can do anything.

Run the first reading command alone and look at what it produces, then add the next | stage and run it again. Each stage’s output is the next stage’s input, so this shows what every command contributes. Select-Object here only picks the columns to show:

Command
PS> Get-ChildItem -Recurse -File | Select-Object -Property Name, Length

Output


Name              Length
----              ------
notes.txt           4000
photo.jpg        3300000
app.log          2100000
old.log           800000
archive.zip     12600000
q3-summary.xlsx  4200000

Add the sort, and the largest files rise to the top:

Command
PS> Get-ChildItem -Recurse -File | Sort-Object -Property Length -Descending | Select-Object -Property Name, Length

Output


Name              Length
----              ------
archive.zip     12600000
q3-summary.xlsx  4200000
photo.jpg        3300000
app.log          2100000
old.log           800000
notes.txt           4000

See what the objects carry

PowerShell passes objects between commands, not text. Get-Member lists what an object holds, which is where a name like Length in the example comes from:

Command
PS> Get-ChildItem -File | Get-Member -MemberType Property

Output


   TypeName: System.IO.FileInfo

Name              MemberType Definition
----              ---------- ----------
Attributes        Property   System.IO.FileAttributes Attributes {get;set;}
CreationTime      Property   datetime CreationTime {get;set;}
…
Length            Property   long Length {get;}
LinkTarget        Property   string LinkTarget {get;}
Name              Property   string Name {get;}
UnixFileMode      Property   System.IO.UnixFileMode UnixFileMode {get;set;}

Find the cmdlet for your own task

The same questions work forward, from a task to the command that does it. Cmdlet names are a verb and a noun, so search by either:

Command
PS> Get-Command -Noun Item -Module Microsoft.PowerShell.Management

Output


CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Cmdlet          Clear-Item                                         7.0.0.0    Microsoft.PowerShell.…
Cmdlet          Copy-Item                                          7.0.0.0    Microsoft.PowerShell.…
Cmdlet          Get-Item                                           7.0.0.0    Microsoft.PowerShell.…
Cmdlet          Invoke-Item                                        7.0.0.0    Microsoft.PowerShell.…
Cmdlet          Move-Item                                          7.0.0.0    Microsoft.PowerShell.…
Cmdlet          New-Item                                           7.0.0.0    Microsoft.PowerShell.…
Cmdlet          Remove-Item                                        7.0.0.0    Microsoft.PowerShell.…
Cmdlet          Rename-Item                                        7.0.0.0    Microsoft.PowerShell.…
Cmdlet          Set-Item                                           7.0.0.0    Microsoft.PowerShell.…

Get-Command -Verb Sort, Select, Group finds the commands that sort, pick and group, plus Select-String and Select-Xml for searching text and XML. From there, Get-Help and Get-Member take you the rest of the way.

Expected result

The one-liner becomes a script you can read, and running it gives the same answer it was copied to give:

Command
PS> Get-ChildItem -Recurse -File | Sort-Object -Property Length -Descending | Select-Object -First 3 -Property Name, @{Name = 'MB'; Expression = { [math]::Round($_.Length / 1MB, 1) }}

Output


Name                MB
----                --
archive.zip     12.000
q3-summary.xlsx  4.000
photo.jpg        3.100

Common failures

sort: invalid option -- D

Error

sort: invalid option -- D
Usage: sort [-bcCdfigMmnrsuz] [-kPOS1[,POS2] ... ] [+POS1 [-POS2]] [-S memsize] [-T tmpdir] [-t separator] [-o outfile] [--batch-size size] [--files0-from file] [--heapsort] [--mergesort] [--radixsort] [--qsort] [--mmap] [--parallel thread_no] [--human-numeric-sort] [--version-sort] [--random-sort [--random-source file]] [--compress-program program] [file ...]

The script was written for Windows, where sort is an alias for Sort-Object. On macOS the name runs the system’s sort program instead. Use the full cmdlet name:

PowerShell
Sort-Object -Property Length -Descending

Get-Help shows only partial help

Error

Get-Help cannot find the Help files for this cmdlet on this computer. It is displaying only partial help.

The help files aren’t installed. Run Update-Help once, or read the same page in a browser:

PowerShell
Get-Help Sort-Object -Online

Tested with

PowerShell
7.6.6
OS
macOS 27.0
Date
October 2026

New articles, by email.

A short note when something new is published.