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 missing

Files and folders

OperationPowerShellBash equivalent
List filesGet-ChildItem or ls (alias)ls
List hidden tooGet-ChildItem -Forcels -a
Current directoryGet-Location or pwdpwd
Change directorySet-Location <path> or cd <path>cd <path>
Make folderNew-Item -ItemType Directory -Path foomkdir foo
Make nested foldersNew-Item -ItemType Directory -Force foo\bar\bazmkdir -p foo/bar/baz
Make fileNew-Item foo.txt -ItemType Filetouch foo.txt
Delete fileRemove-Item foo.txtrm foo.txt
Delete folder + contentsRemove-Item -Recurse -Force folderrm -rf folder
CopyCopy-Item src dstcp src dst
Move / renameMove-Item src dstmv src dst
Print file contentsGet-Content foo.txt or cat (alias)cat foo.txt
First N linesGet-Content foo.txt -TotalCount 10head -10 foo.txt
Last N linesGet-Content foo.txt -Tail 10tail -10 foo.txt
Tail followGet-Content foo.txt -Wait -Tail 10tail -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 path

Pipes 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 PID

Key aliases:

  • ? = Where-Object
  • % = ForEach-Object
  • select = Select-Object
  • sort = 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 git

After 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 install

Run 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 -rf is not native. It’s an alias for Remove-Item but doesn’t accept the -rf flag pattern. Use Remove-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 utf8 to Out-File / Set-Content / Get-Content.
  • Set-Content and Out-File default to UTF-16 LE with BOM. That can break tools expecting plain UTF-8. Always -Encoding utf8 (or utf8NoBOM in PS 7+) for files other tools will read.
  • Pipes pass objects, not strings. Sometimes you want the string representation: ... | Out-String converts before further processing.
  • ExecutionPolicy errors when running scripts. Run Set-ExecutionPolicy RemoteSigned -Scope CurrentUser once.
  • Tab completion is case-INsensitive for cmdlet names but the actual names are PascalCase (Get-Process, not get-process).
  • $null -eq $x is safer than $x -eq $null — the former works correctly on arrays.
  • Aliases break in scripts you share. ls works interactively but in a .ps1 script intended for portability, prefer Get-ChildItem. Aliases can be redefined; cmdlet names can’t.

See also


Sources