Coding · Programming languages
PowerShell tutorial for beginners
Short answer
PowerShell is Microsoft's cross-platform shell and scripting language for automation. Its commands, called cmdlets, pass .NET objects through the pipeline instead of plain text, so you can filter, sort and export data without parsing. This page covers installing PowerShell 7, running scripts, cmdlets, variables, operators, control flow, functions, collections and error handling.
Open Regex testerWhat PowerShell is and what it is used for
PowerShell is a cross-platform shell and scripting language from Microsoft, built on .NET. Unlike traditional shells, which pass plain text between commands, PowerShell passes .NET objects with properties and methods, so you can filter and sort data without parsing text.
System administrators and DevOps engineers use PowerShell to automate Windows, manage Active Directory, Microsoft 365 and Azure, and write build and deployment scripts. There are two editions: Windows PowerShell 5.1, which is built into Windows, and PowerShell 7 and later (command name pwsh), which is open source and runs on Windows, Linux and macOS. This tutorial targets PowerShell 7.
Install PowerShell and run Hello, world
On Windows, install PowerShell 7 with winget install --id Microsoft.PowerShell --source winget; on macOS and Linux, use Homebrew or the package manager described in Microsoft's install guide. Scripts are text files with the .ps1 extension.
# hello.ps1
Write-Output "Hello, world!"pwsh ./hello.ps1
# Hello, world!On Windows, the execution policy may block scripts. Set-ExecutionPolicy -Scope CurrentUser RemoteSigned allows your own local scripts while still requiring downloaded scripts to be signed. Visual Studio Code with the PowerShell extension is the recommended editor.
Cmdlets and the pipeline
Commands are called cmdlets and follow a Verb-Noun naming pattern, such as Get-Process, Set-Location or New-Item. Three cmdlets help you explore everything else: Get-Command, Get-Help and Get-Member.
Get-Command -Noun Process # list commands that work with processes
Get-Help Get-ChildItem -Examples # show usage examples
# five processes using the most memory
Get-Process |
Sort-Object -Property WorkingSet64 -Descending |
Select-Object -First 5 -Property Name, Id, WorkingSet64
# files larger than 1 MB in the current folder
Get-ChildItem -File | Where-Object { $_.Length -gt 1MB } |
ForEach-Object { "{0} {1:N0} KB" -f $_.Name, ($_.Length / 1KB) }
Get-Date | Get-Member -MemberType Property # inspect an objectInside a script block, $_ (or $PSItem) is the current object in the pipeline. Because objects keep their types, Sort-Object sorts numbers numerically and dates chronologically without extra work.
Variables and types
Variables start with $. They hold any .NET object, and you can add a type constraint in square brackets. Double-quoted strings expand variables and $( ) expressions; single-quoted strings are literal.
$name = "Ada"
[int]$count = 3
$price = 19.99 # System.Double
$isAdmin = $false
$today = Get-Date
"Hello, $name" # Hello, Ada
"Next: $($count + 1)" # Next: 4
'No $expansion here' # No $expansion here
$price.GetType().Name # Double
$today.ToString("yyyy-MM-dd") # for example 2026-10-31
[int]"42" + 1 # 43 (explicit conversion)| Type | Example | Notes |
|---|---|---|
[int] | 42 | 32-bit integer |
[double] | 3.14 | floating point; [decimal] for money |
[string] | "text" | case-insensitive comparisons by default |
[bool] | $true, $false | automatic variables, not keywords |
[datetime] | Get-Date | full .NET DateTime object |
[array], [hashtable] | @(1, 2), @{ a = 1 } | collections, covered below |
Operators and control flow
Comparison operators are words, not symbols: -eq, -ne, -gt, -lt, -ge, -le, -like (wildcards), -match (regular expressions) and -contains. String comparisons are case-insensitive unless you use the c versions such as -ceq.
$temp = 22
if ($temp -gt 30) {
"hot"
} elseif ($temp -ge 15) {
"mild" # mild
} else {
"cold"
}
switch ("report.csv") {
{ $_ -like "*.csv" } { "CSV file" } # CSV file
{ $_ -like "*.txt" } { "text file" }
default { "other" }
}
foreach ($n in 1..3) { "n = $n" } # n = 1, n = 2, n = 3
for ($i = 0; $i -lt 2; $i++) { "i = $i" } # i = 0, i = 1
$k = 3
while ($k -gt 0) { $k-- }
"Hello" -eq "hello" # TrueFunctions and parameters
Functions use param() blocks to declare typed, named parameters. Adding [CmdletBinding()] gives a function the same common parameters as built-in cmdlets, such as -Verbose. Any output that is not captured becomes the function's return value.
function Get-Greeting {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Name,
[string]$Greeting = "Hello"
)
Write-Verbose "Building greeting for $Name"
"$Greeting, $Name!"
}
Get-Greeting -Name "Ann" # Hello, Ann!
Get-Greeting -Name "Bo" -Greeting "Hi" # Hi, Bo!
Get-Greeting "Cy" -Verbose # also prints the verbose messageCall functions like cmdlets, with spaces between arguments: Get-Greeting -Name Ann. Writing Get-Greeting("Ann", "Hi") passes a single array argument, a frequent surprise for people coming from other languages.
Arrays, hashtables and custom objects
Arrays are created with @( ) or a comma-separated list. Hashtables, written @{ }, store key-value pairs. [pscustomobject] turns a hashtable into an object that works well with Sort-Object, Format-Table and Export-Csv.
$langs = @("PowerShell", "Python")
$langs += "Go" # creates a new, longer array
$langs.Count # 3
$langs[0] # PowerShell
$langs -contains "Go" # True
$ports = @{ http = 80; https = 443 }
$ports["ssh"] = 22
$ports.https # 443
$ports.Keys.Count # 3
$user = [pscustomobject]@{ Name = "Ann"; Age = 31 }
$user.Name # Ann
$people = @(
[pscustomobject]@{ Name = "Bo"; Age = 25 }
$user
)
$people | Sort-Object Age | Format-TableAdding to an array with += copies the whole array each time. For large loops, collect the loop output directly ($result = foreach (...) { ... }) or use a [System.Collections.Generic.List[object]].
Error handling
PowerShell has terminating errors, which stop execution, and non-terminating errors, which print a message and continue. try/catch/finally only catches terminating errors, so add -ErrorAction Stop to a cmdlet when you want to catch its failures.
try {
$content = Get-Content -Path "missing.txt" -ErrorAction Stop
}
catch [System.Management.Automation.ItemNotFoundException] {
Write-Warning "File not found: $($_.Exception.Message)"
}
catch {
Write-Error "Unexpected error: $_"
}
finally {
"cleanup done"
}
$ErrorActionPreference = "Stop" # make all errors terminating in this script
throw "Something went wrong" # raise your own terminating errorInside catch, $_ is the error record. Setting $ErrorActionPreference = "Stop" at the top of a script is a common way to fail fast.
A small complete script: disk usage report
This script lists the largest files under a folder, groups them by extension and exports a CSV report. It combines parameters, the pipeline, calculated properties and error handling.
# report.ps1
param(
[string]$Path = ".",
[int]$Top = 5
)
$ErrorActionPreference = "Stop"
$files = Get-ChildItem -Path $Path -File -Recurse
"Largest $Top files:"
$files | Sort-Object Length -Descending | Select-Object -First $Top |
Format-Table Name, @{ Name = "SizeKB"; Expression = { [math]::Round($_.Length / 1KB, 1) } }
$byExt = $files | Group-Object Extension | ForEach-Object {
[pscustomobject]@{
Extension = if ($_.Name) { $_.Name } else { "(none)" }
Files = $_.Count
TotalMB = [math]::Round(($_.Group | Measure-Object Length -Sum).Sum / 1MB, 2)
}
} | Sort-Object TotalMB -Descending
$byExt | Format-Table
$byExt | Export-Csv -Path "usage.csv" -NoTypeInformation
"Report saved to usage.csv"pwsh ./report.ps1 -Path ~/Documents -Top 3Where to go next
- PowerShell 101 and the PowerShell documentation on Microsoft Learn.
Get-Help about_*in the shell, for exampleGet-Help about_Operators; runUpdate-Helpfirst.- The PowerShell Gallery, the official repository of modules, used with
Install-Module. - PSScriptAnalyzer, a linter that checks scripts against recommended practices.
FAQ
What is the difference between PowerShell and Windows PowerShell?
Windows PowerShell 5.1 is the older edition built into Windows and based on .NET Framework. PowerShell 7 and later is the cross-platform, open-source edition based on modern .NET, started with the pwsh command.
Is PowerShell the same as Command Prompt?
No. Command Prompt (cmd.exe) runs batch commands and passes text. PowerShell is a full scripting language that passes objects, although it can still run most command-line programs.
Can PowerShell run on Linux and macOS?
Yes. PowerShell 7 and later runs on Windows, Linux and macOS, although some Windows-specific modules are only available on Windows.
Why can't I run my .ps1 script?
The execution policy on Windows may block it. Run Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, and start scripts with a path, such as ./script.ps1.
What does $_ mean in PowerShell?
$_ (also $PSItem) is the current object in the pipeline, used inside script blocks for cmdlets such as Where-Object and ForEach-Object, and the current error inside catch.