PowerShell occupies a unique place in the scripting world. It looks like a shell (you type commands), behaves like a programming language (real types, real exceptions), and treats command output as objects rather than text. That last bit is what makes it different from Bash, and once it clicks the rest follows.
PowerShell 5.1 vs PowerShell 7
| Windows PowerShell 5.1 | PowerShell 7 (pwsh) | |
|---|---|---|
| Released | 2016, ships with Windows | 2020, ongoing yearly releases |
| Runtime | Full .NET Framework 4.x | .NET 8/9 (cross-platform) |
| Platforms | Windows only | Windows, macOS, Linux |
| Status | In maintenance — no new features | Active development |
| Executable | powershell.exe | pwsh / pwsh.exe |
Use PowerShell 7 for new work. 5.1 is still everywhere on Windows servers and required for some legacy modules (notably older Exchange / Skype management), but the modern Azure modules, Microsoft Graph PowerShell, and most third-party content are all cross-platform pwsh 7 today.
Install:
- Windows —
winget install Microsoft.PowerShellor the MSI from GitHub releases - macOS —
brew install --cask powershell - Linux — package from
packages.microsoft.com(deb / rpm)
Start it with pwsh. Inside, $PSVersionTable confirms the version.
Cmdlets — Verb-Noun
Every PowerShell command is a cmdlet with a name in the form Verb-Noun:
Get-Process # list running processes
Get-Service # list services
Get-ChildItem # list directory contents (alias: ls, dir)
Set-Location # change directory (alias: cd)
New-Item # create file or folder
Remove-Item # delete (alias: rm)
Copy-Item # copy (alias: cp)
Invoke-WebRequest # HTTP request (alias: curl, wget — careful, replaces real curl/wget)
Get-Help # show help
Get-Command # discover commands
The approved verb list (Get-Verb) is small and enforced — Microsoft's style guide. Get- reads, Set- writes, New- creates, Remove- deletes, Invoke- runs an action, Test- returns boolean, Find- searches.
This consistency is one of PowerShell's strengths — guess a command name and you are usually right.
Aliases
PowerShell ships with aliases that make it feel like other shells:
| Alias | Real cmdlet |
|---|---|
ls, dir | Get-ChildItem |
cd | Set-Location |
pwd | Get-Location |
cat, type | Get-Content |
rm, del | Remove-Item |
cp, copy | Copy-Item |
echo | Write-Output |
% | ForEach-Object |
? | Where-Object |
Use aliases interactively; use full cmdlet names in scripts (readability and compatibility — alias mappings can differ on non-Windows pwsh).
Parameters
Cmdlets take named parameters with -Name syntax:
Get-ChildItem -Path C:\Users -Recurse -Filter *.log
Stop-Process -Name notepad -Force
Test-Connection -TargetName github.com -Count 4
Parameters can be positional too, but explicit is preferred in scripts. Tab completion expands parameter names — type Get-Process - then Tab.
Variables and Types
$name = "kube"
$count = 42
$names = @("alice", "bob", "carol")
$user = @{ Name = "Alice"; Age = 30 } # hashtable
# Types are inferred but can be specified
[int]$port = 8080
[string]$path = "/etc/hosts"
[datetime]$now = Get-Date
# Strings — double-quoted interpolate, single-quoted don't
$greeting = "Hello, $name!" # → Hello, kube!
$literal = 'Hello, $name!' # → Hello, $name!
# Subexpressions in strings
"There are $($names.Count) names." # → There are 3 names.
Control Flow
if ($port -gt 1024) { "non-privileged" } else { "privileged" }
for ($i = 0; $i -lt 5; $i++) { "$i" }
foreach ($n in $names) { "Hello, $n" }
while ($true) { Start-Sleep 1; if (Test-Path stop.txt) { break } }
switch ($status) {
"pending" { "Waiting..." }
"running" { "Active" }
"done" { "Complete" }
default { "Unknown: $status" }
}
Comparison operators are -eq, -ne, -lt, -le, -gt, -ge, -like (wildcards), -match (regex), -contains, -in. (Yes, not == — = is assignment only.)
The Three Commands You Use Constantly
| Command | Purpose |
|---|---|
Get-Help <cmdlet> -Full | Show full help for a cmdlet, including examples. Get-Help -Online opens the official docs. |
Get-Command -Module Az.* -Verb New | Discover cmdlets matching a pattern. |
Get-Process | Get-Member | Inspect what properties and methods an object has. The single most useful command in PowerShell. |
If you remember nothing else from this lesson, remember Get-Member. Every time you pipe one cmdlet to another and wonder "what fields does this object have," you pipe to Get-Member.
PSReadLine — the Interactive Polish
The interactive shell is far better than it has any right to be thanks to PSReadLine (bundled with pwsh 7+):
- Syntax highlighting as you type
- History search (Ctrl-R like bash)
- Inline prediction (suggestions from history shown in grey)
- Multi-line editing with paren-matching
Turn on predictive history once: Set-PSReadLineOption -PredictionSource HistoryAndPlugin. You will not go back.
Execution Policy
One Windows-specific gotcha — PowerShell scripts (.ps1) are blocked by default on Windows. Loosen it for development:
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
RemoteSigned means local scripts run; downloaded scripts must be signed. It is the standard developer choice. (On non-Windows pwsh this does not apply.)
Profile and Customisation
Each user has a startup profile script. Find it with $PROFILE, edit with code $PROFILE. Common contents:
# Aliases
Set-Alias -Name g -Value git
# Functions
function Get-PublicIP { (Invoke-RestMethod ifconfig.me/ip).Trim() }
# Prompt
function prompt { "PS $(Get-Location)> " }
# Module imports for daily use
Import-Module posh-git
For a fancy prompt, oh-my-posh is the cross-platform choice — it matches starship in capability and theming.
Where to Read More
- PowerShell official docs — excellent, kept current
- PowerShell on GitHub
- Learn PowerShell in a Month of Lunches — the classic introduction (Don Jones, James Petty, Travis Plunk)
- PowerShell 101 — free Microsoft Learn course
The next lesson covers what makes PowerShell genuinely different from Bash and CMD — the object pipeline.