PowerShell cheat sheet: language & syntax
This sheet is the grammar layer: the pieces you need before any real script. It targets the two runtimes you actually have — Windows PowerShell 5.1 (powershell.exe) and PowerShell 7 (pwsh) — and everything below behaves identically in both unless noted.
Two ideas run through all of it. First, PowerShell touches objects: every command emits .NET objects, and the pipeline passes objects, not text (this is the single biggest difference from cmd). Second, syntax is verb-noun and parameter-flag: Get-Service -Name BITS reads as an action against a thing, and parameters are -Flag Value — never cmd's /flag value.
The building blocks
| Type | Created with | Gotcha |
|---|---|---|
| string | 'single' or "double" quotes | double quotes expand variables; single quotes are literal |
| int / decimal | 42, 1.5, 0x2A | division always returns a double: 5 / 2 is 2.5, not 2 |
| bool | $true, $false | comparisons resolve to bool; -and binds tighter than -or |
| array | @(1,2,3) or 1,2,3 | a single-item array needs @( ); an object unrolls down the pipeline |
| hashtable | @{ Name = 'BITS' } | key values use = ; separate entries with ; or line breaks |
| pscustomobject | [pscustomobject]@{ Name = 'BITS' } | the shaped object form of a hashtable — good for CSV export |
Operators: comparison, matching and logic
- -eq / -ne
- equals / not-equals. Case-insensitive by default; -ceq / -cne for exact case
- -lt / -gt / -le / -ge
- less than, greater than, and their inclusive forms — numbers, dates and strings
- -like / -notlike
- wildcard match: * any run, ? single char. Case-insensitive
- -match / -notmatch
- regular expression match; $Matches receives the groups
- -contains / -in
- array membership: 2 -in @(1,2,3); -notcontains for the negative
- -and / -or / -not
- logical tests; use parentheses to control grouping
- -replace
- regex replace: "a-b" -replace "-", "+"
- -is / -isnot
- type test: $_.Size -is [int]
Pipelines: objects, not text
Get-Service | Where-Object Status -eq "Running" | Select-Object Name, Status
# Scriptblock form — $_ is the current object in the pipeline
Get-Process | Where-Object { $_.ProcessName -like "*sql*" }
# Simplified form — property, comparison operator, value
Get-Process | Where-Object ProcessName -like "*sql*"
# ForEach-Object transforms each item
1..10 | ForEach-Object { $_ * $_ }
# Aliases: ? is Where-Object, % is ForEach-Object, gm is Get-Member
Get-Service | ? Status -eq "Running" | % Name | gm
$_ (also available as $PSItem) is the automatic variable that holds the current object in the pipeline. It only exists inside script blocks passed to Where-Object, ForEach-Object and friends. The simplified filter syntax — Where-Object Status -eq "Running" — is PowerShell 3+ and works identically in 5.1 and 7.Quotes and here-strings
The four string forms, in order of how often you will type them
- 'single quotes'
- literal text — variables are NOT expanded. Default for fixed strings
- "double quotes"
- string expansion: $name and $(command) are evaluated
- @'...'@ here-string
- multi-line literal block; closing @' must be at the start of a line
- @"..."@ here-string
- multi-line expanded block; use when a text block contains both $ and line breaks
- ` (backtick)
- escape character inside strings: `" quotes the quote, `$ quotes the dollar sign
Arrays, hashtables and splatting
# Array — comma syntax, or @() for explicit arrays
$servers = @("srv01", "srv02", "srv03")
# Hashtable — note the = between key and value
$config = @{
Name = "BITS"
State = "Running"
Secure = $true
}
# Splatting — @ in front of a variable whose value is a hashtable
$params = @{ Name = "BITS"; StartupType = "Automatic" }
Set-Service @params
# Shaped object — hashtable cast to an object with real properties
$result = [pscustomobject]@{ Server = "srv01"; FreeGB = 42.5 }
@params, not $params — the @ marks the hashtable as a parameter bag. Without it, Get-Service $params tries to pass the whole hashtable as the name. Arrays and hashtables are also why Get-Service | Where-Object Status -eq "Running" can sit in one line while a full script grows a line per role: add data, then filter, then select.Aliases and getting help
| Alias | Cmdlet | Note |
|---|---|---|
| gci, ls | Get-ChildItem | ls works in cmd and PowerShell but means different things |
| gp | Get-ItemProperty | registry values — not a PSDrive listing |
| gm | Get-Member | the object inspector: pipe anything into gm |
| sls | Select-String | grep-style search inside files |
| sc | Set-Content | NOT sc.exe — that is Service Control (a different tool entirely) |
| ps, gps | Get-Process | command name shadows the process list itself |
| iex | Invoke-Expression | executes a string — only run strings you trust |
| ?, % | Where-Object, ForEach-Object | short forms of piping workhorses |
Help and discovery commands
- Get-Help <cmdlet>
- local help; add -Examples and -Detailed for more depth
- Get-Help <cmdlet> -Online
- opens the Microsoft Learn reference page in a browser
- Get-Command -Verb Get
- lists every cmdlet whose verb is, say, Get
- Get-Command -Noun Service
- lists cmdlets operating on a noun — Get-Service, Set-Service, New-Service
- Update-Help
- downloads help files from the internet; needs elevation for system-wide modules
PowerShell 7 vs Windows PowerShell 5.1
pwsh) is a cross-platform rewrite on modern .NET: its 7.4 line runs on .NET 8, 7.5 on .NET 9, and the same shell works on Linux and macOS with different built-ins. Windows PowerShell 5.1 is the .NET Framework 4.x-era shell that ships in Windows 10/11, still the default console, and maintained but no longer the actively developed one. $PSVersionTable.PSVersion (or $PSVersionTable alone) always tells you which you are in. The rule that matters: core Microsoft.PowerShell.* cmdlets are compatible, but Windows-only modules are not — Get-CimInstance Win32_*, Get-NetAdapter, Get-NetIPAddress and friends simply do not exist in PowerShell 7 on Linux, because their providers ship in Windows. On Windows, both runtimes load them the same way. Get disk space with PowerShell and list installed software use only cross-compatible cmdlets and run in both.FAQ
Is PowerShell 7 the same as Windows PowerShell?
No. Windows PowerShell 5.1 is the legacy, Windows-only shell that ships in Windows 10/11; PowerShell 7 (pwsh) is the modern cross-platform rewrite running on modern .NET and installs side by side with it. Common cmdlets work in both, but 5.1 cannot load PowerShell 7 modules and PowerShell 7 on Linux cannot load Windows-only modules like Get-NetAdapter or the Win32_* CIM classes. Check which one you are in with $PSVersionTable.
What does $_ mean?
$_ (or $PSItem) is the automatic variable holding the current object inside a pipeline script block. In Where-Object { $_.Status -eq 'Running' } the filter tests each incoming object through $_, and ForEach-Object { $_.Name } reads the same variable to transform each item. It exists only inside script blocks passed to pipelining cmdlets.
What is the difference between 'single quotes' and "double quotes"?
Double quotes expand the string: "$name" prints the value of the variable, and "$(Get-Date)" runs the expression. Single quotes are literal: '$name' prints the four characters $name. Multi-line needs here-strings — @'...'@ for literal, @"..."@ for expanded — and the closing marker must sit at the start of its own line.
What do @( ) and @{ } mean in PowerShell?
@( ) creates an array, @{ } creates a hashtable. The same @ token in front of a variable name — @params instead of $params — is splatting: it unpacks the hashtable into named parameters of a cmdlet, which is how you pass a reusable bind of options like $params = @{ Name='BITS'; StartupType='Automatic' }.
Related tools
- IPv4 subnet calculator — break any CIDR block into network, range, broadcast and usable hosts.
- IP range to CIDR — turn an arbitrary address range into its minimal covering CIDR blocks.
- VLSM calculator — split a block into right-sized subnets by host requirements.