PowerShell cheat sheet
Status: 🟩 COMPLETE Last updated: 2026-06-21 Plain-English tagline: PowerShell commands for daily dev work on Windows, with the Bash equivalents alongside so you can mentally translate tutorials. For the why, read PowerShell vs Bash.
Why this exists
Most internet tutorials use Bash syntax (Mac/Linux). On Windows, PowerShell is the default, and the commands differ. This sheet gives you the PowerShell version of the operations you’d actually do.
If you’d rather use Bash on Windows, install Git Bash (ships with Git) or WSL (real Linux inside Windows). Both work fine; this sheet covers PowerShell because it’s the Windows default and what Claude Code uses by default on your machine.
One-time setup
# Allow scripts to run (the npm/node ecosystem needs this)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# Check version
$PSVersionTable.PSVersion # PowerShell 5.1 (default on Windows 10/11) or 7.x
# Open profile (where you put aliases + persistent config)
notepad $PROFILE # creates if missingFiles and folders
| Operation | PowerShell | Bash equivalent |
|---|---|---|
| List files | Get-ChildItem or ls (alias) | ls |
| List hidden too | Get-ChildItem -Force | ls -a |
| Current directory | Get-Location or pwd | pwd |
| Change directory | Set-Location <path> or cd <path> | cd <path> |
| Make folder | New-Item -ItemType Directory -Path foo | mkdir foo |
| Make nested folders | New-Item -ItemType Directory -Force foo\bar\baz | mkdir -p foo/bar/baz |
| Make file | New-Item foo.txt -ItemType File | touch foo.txt |
| Delete file | Remove-Item foo.txt | rm foo.txt |
| Delete folder + contents | Remove-Item -Recurse -Force folder | rm -rf folder |
| Copy | Copy-Item src dst | cp src dst |
| Move / rename | Move-Item src dst | mv src dst |
| Print file contents | Get-Content foo.txt or cat (alias) | cat foo.txt |
| First N lines | Get-Content foo.txt -TotalCount 10 | head -10 foo.txt |
| Last N lines | Get-Content foo.txt -Tail 10 | tail -10 foo.txt |
| Tail follow | Get-Content foo.txt -Wait -Tail 10 | tail -f foo.txt |
Finding and searching
# Find files by name (recursive)
Get-ChildItem -Recurse -Filter "*.tsx"
# Find files by pattern
Get-ChildItem -Recurse | Where-Object { $_.Name -match "auth.*\.ts" }
# Search file contents (Bash: grep)
Select-String -Path "**/*.ts" -Pattern "TODO"
# Find a command
Get-Command npm # like Bash: which npm
(Get-Command npm).Source # just the pathPipes and filtering
PowerShell pipes pass objects, not text. This is more powerful than Bash but takes getting used to.
# List processes, filter by name, sort by memory, take top 5
Get-Process | Where-Object { $_.Name -like "node*" } | Sort-Object WorkingSet -Descending | Select-Object -First 5
# Equivalent shorthand using aliases
ps | ? { $_.Name -like "node*" } | sort WorkingSet -desc | select -First 5
# Get the property of one object as text
(Get-Process npm).Id # just the PIDKey aliases:
?=Where-Object%=ForEach-Objectselect=Select-Objectsort=Sort-Object
Variables and strings
$name = "George" # assignment
$count = 42
"Hello, $name" # double quotes interpolate
'Hello, $name' # single quotes literal
# Multi-line string (here-string)
$msg = @"
Hello, $name
This has interpolation.
"@
# Literal multi-line
$msg = @'
Hello, $name
This is literal (no interpolation).
'@Critical: the closing "@ or '@ MUST be at column 0 (no leading whitespace) on its own line, or PowerShell errors.
Environment variables
$env:NODE_ENV # read
$env:NODE_ENV = "development" # set for this session
[Environment]::GetEnvironmentVariable("PATH", "User") # persistent user PATH
[Environment]::SetEnvironmentVariable("MYVAR", "value", "User") # persistent set.env files: PowerShell doesn’t load these automatically. Tools like Next.js read them. For ad-hoc loading:
Get-Content .env | ForEach-Object {
$key, $value = $_ -split '=', 2
if ($key -and -not $key.StartsWith("#")) {
Set-Item -Path "env:$key" -Value $value
}
}Process management
Get-Process # all processes
Get-Process node # filtered
Stop-Process -Id <pid> # kill by PID
Stop-Process -Name node # kill by name
Stop-Process -Name node -Force # force-kill
# Find what's using port 3000
Get-NetTCPConnection -LocalPort 3000 | Select-Object OwningProcess
# Then:
Stop-Process -Id <pid>Aliases and shortcuts
PowerShell has many Bash-style aliases built in: ls, cd, pwd, cat, cp, mv, rm, ps, kill, cls (clear), man (Get-Help).
To see all aliases: Get-Alias.
To add your own (in $PROFILE):
function dev { npm run dev }
function build { npm run build }
Set-Alias g gitAfter saving, reload the profile: . $PROFILE.
Common dev workflows
Open the current folder in VS Code
code .Nuke node_modules and reinstall (the npm reset)
Remove-Item -Recurse -Force node_modules, package-lock.json
npm installRun a one-line script (multi-statement)
# PowerShell uses ; not && for unconditional chaining
npm run build; if ($?) { npm run start }
# Or use `;` and check exit code yourself
git pull; if ($LASTEXITCODE -eq 0) { npm install }&& and || only work in PowerShell 7+ (not 5.1).
Background a command
Start-Process npm -ArgumentList "run","dev" # detached process
# Or use background jobs:
Start-Job -ScriptBlock { npm run dev }
Get-Job
Stop-Job <id>Heredoc-like git commit
git commit -m @'
First line.
Second line, no interpolation.
'@The closing '@ must be at column 0 on its own line.
Common gotchas
&&and||don’t work in PowerShell 5.1 (the Windows default). Use;and check$?or$LASTEXITCODE. PowerShell 7+ supports both.- Single quotes are literal; double quotes interpolate. Opposite of what some other shells do.
'$name'is the string$name;"$name"is the value. - The closing
'@of a here-string MUST be at column 0. Indenting it is a parser error. rm -rfis not native. It’s an alias forRemove-Itembut doesn’t accept the-rfflag pattern. UseRemove-Item -Recurse -Force.- Background processes don’t inherit refreshed PATH. If you install a tool, then
Start-Job { the-new-tool }, the job often can’t find it. Refresh:$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - PowerShell 5.1 reads files as Windows-1252. When working with UTF-8 files, pass
-Encoding utf8toOut-File/Set-Content/Get-Content. Set-ContentandOut-Filedefault to UTF-16 LE with BOM. That can break tools expecting plain UTF-8. Always-Encoding utf8(orutf8NoBOMin PS 7+) for files other tools will read.- Pipes pass objects, not strings. Sometimes you want the string representation:
... | Out-Stringconverts before further processing. ExecutionPolicyerrors when running scripts. RunSet-ExecutionPolicy RemoteSigned -Scope CurrentUseronce.- Tab completion is case-INsensitive for cmdlet names but the actual names are PascalCase (
Get-Process, notget-process). $null -eq $xis safer than$x -eq $null— the former works correctly on arrays.- Aliases break in scripts you share.
lsworks interactively but in a.ps1script intended for portability, preferGet-ChildItem. Aliases can be redefined; cmdlet names can’t.
See also
- PowerShell vs Bash 🟩 — the textbook
- command line 🟩
- Windows dev environment 🟩
- Terminals & emulators 🟩
- Files and folders 🟩
- Git cheat sheet 🟩
- npm cheat sheet 🟩