Skip to content

PowerShell Essentials

The modern Windows shell. Unlike CMD, PowerShell pipes objects, not text: Get-Process python | Stop-Process filters and acts on real data. If you do serious Windows automation, this is your shell.

Table of Contents

Installation / Setup

Windows 10/11 ships PowerShell 5.1 (Windows PowerShell) and pwsh 7+ (PowerShell Core, installable) — prefer 7+. Open via Terminal, or:

pwsh                          # PowerShell 7
$PSVersionTable.PSVersion     # version
Get-Command -Name *process*   # discover cmdlets
Get-Help Get-Process          # docs (add -Online for web)

CMD vs PowerShell in one glance: CMD is text + %VAR% + copy; PowerShell is objects + $VAR + Copy-Item. Most "verbs" come from the standard verb list: Get-, Set-, New-, Remove-, Start-, Stop-, Test-.

Core Syntax

Linux ↔ Windows (PowerShell) map

Task Linux PowerShell (alias) CMD
list files ls -la Get-ChildItem (ls, dir) dir
change dir cd /data Set-Location (cd) cd
print dir pwd Get-Location (pwd) cd
copy cp -r a b Copy-Item -Recurse a b (cp) copy
move/rename mv a b Move-Item (mv) / Rename-Item move/ren
delete rm -r f Remove-Item -Recurse -Force (rm, del) del, rmdir
view file cat f Get-Content (cat, type) type
tail tail -f f Get-Content f -Tail 20 -Wait —
find text grep Select-String (grep) findstr
env var echo $VAR $env:VAR / Get-ChildItem Env: %VAR%
kill proc kill PID Stop-Process -Id 1234 (kill) taskkill
which which cmd Get-Command cmd (gcm) where

Objects & pipelines (the big idea)

Get-Process python                      # process objects, not text
Get-Process | Where-Object CPU -gt 10   # filter objects (like grep on steroids)
Get-Process | Sort-Object CPU -Descending | Select-Object -First 5
Get-Process | Select-Object Name, Id, CPU | Export-Csv procs.csv

Pipeline stages operate on objects with properties — you filter/sort/select properties, not lines.

Variables, arrays, hashes

$name = "Ada"
$count = 42
$files = Get-ChildItem *.csv            # array of objects
$files.Count
$config = @{ lr = 0.01; epochs = 10 }   # hashtable (dict)
$config["lr"]
$env:PATH                               # environment: $env: prefix
$env:MY_VAR = "value"                   # session only
[Environment]::SetEnvironmentVariable("MY_VAR", "value", "User")  # persistent

Common Operations

ls -Force                              # include hidden
ls -Recurse -Filter *.py
Get-Content app.log -Tail 50           # last 50 lines
Get-Content app.log -Wait              # follow like tail -f
Select-String -Path *.log -Pattern "error" -CaseSensitive:$false

Processes, services & system

Get-Process                            # tasklist equivalent
Get-Process python | Stop-Process      # kill all python
Stop-Process -Id 1234 -Force
Get-Service                            # services
Get-Service sshd | Start-Service
Restart-Service docker
Get-ComputerInfo                       # OS/hardware overview
Get-CimInstance Win32_OperatingSystem  # detailed OS info

Networking

Test-NetConnection google.com -Port 443      # ping + port check (great)
Get-NetTCPConnection -LocalPort 8888          # who listens on 8888
Get-NetTCPConnection -LocalPort 8888 | Select-Object -ExpandProperty OwningProcess
Invoke-WebRequest https://example.com -OutFile page.html    # curl-ish
Invoke-RestMethod https://api.github.com/repos/python/cpython | ConvertTo-Json

Packages (winget & chocolatey)

winget install Python.Python.3.12        # modern installer
winget upgrade --all
winget search pandas
# classic:  choco install python --yes   (after installing chocolatey)

Archives & downloads

Expand-Archive data.zip -DestinationPath data\
Compress-Archive data\ -DestinationPath data.zip

SSH

ssh user@host                          # OpenSSH client ships with Windows
ssh-keygen -t ed25519
Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub   # copy into GitHub/cloud
scp -i $env:USERPROFILE\.ssh\id_ed25519 model.pt user@host:/home/user/

Text vs JSON handling

Get-Content data.json | ConvertFrom-Json | Select-Object -ExpandProperty results
$obj = Get-Content data.json | ConvertFrom-Json
$obj | ConvertTo-Json -Depth 5 | Set-Content out.json

Practical Examples

1. Find and stop the process holding port 8888

$conn = Get-NetTCPConnection -LocalPort 8888 -ErrorAction SilentlyContinue
if ($conn) {
    $pid = $conn.OwningProcess
    "Killing PID $pid ($((Get-Process -Id $pid).Name))"
    Stop-Process -Id $pid -Force
} else {
    "port 8888 is free"
}

2. Export a directory listing to CSV for analysis

Get-ChildItem data -Recurse -File | Select-Object Name, Length, LastWriteTime |
    Export-Csv file_inventory.csv -NoTypeInformation
Import-Csv file_inventory.csv | Where-Object Length -gt 1MB

3. Loop a training command over configs

foreach ($lr in 0.001, 0.0001) {
    foreach ($seed in 1, 2, 3) {
        Start-Process python -ArgumentList "train.py", "--lr", "$lr", "--seed", "$seed" -NoNewWindow
        Start-Sleep -Seconds 2
    }
}

4. Health check then restart a service

if (-not (Test-NetConnection localhost -Port 8000 -InformationLevel Quiet)) {
    Restart-Service myapi
    "service restarted"
} else { "service healthy" }

Common Patterns

# Fail-fast script header
$ErrorActionPreference = "Stop"

# Cross-platform one-liner spirit: every stage transforms objects
Get-ChildItem logs -Filter *.log | ForEach-Object {
    $n = (Select-String -Path $_.FullName -Pattern "ERROR").Count
    [PSCustomObject]@{ File = $_.Name; Errors = $n }
} | Where-Object Errors -gt 0 | Sort-Object Errors -Descending

# Try/catch like other languages
try { Invoke-RestMethod $url } catch { Write-Warning "failed: $_" }

Best Practices

  • Pipe objects, not text — you almost never need format-table mid-pipeline.
  • Use -ErrorAction Stop in scripts so failures don't pass silently.
  • Learn the aliases but type the cmdlets in scripts for readability.
  • Prefer Get-Content -Tail over loading whole log files.
  • Restrict script execution thoughtfully: check Get-ExecutionPolicy; run trusted scripts with -ExecutionPolicy Bypass -File x.ps1 only when you understand them.
  • Windows security defaults block unsigned scripts (ExecutionPolicy) — that's by design; sign or explicitly allow your own.
  • Keep read-only commands (Get-*, Test-NetConnection, Select-String) for probing systems you don't own; never run Stop-Process/Remove-Item against machines without authorization.

Common Mistakes

Mistake Consequence Fix
Using CMD syntax (%VAR%, copy) Literal text / wrong commands $VAR, Copy-Item
Get-Content on a huge file Slow, memory-heavy -Tail N or -ReadCount
Forgetting Select-Object Terminal flooded with objects Select just the properties you need
where.exe confusion where alias ≠ expected Use Get-Command
Unquoted *.csv in some contexts Unexpected glob expansion Quote patterns for -Filter/-Path
Stop-Process without checking Kills the wrong app Filter first: Get-Process \| Where-Object ...
Editing $PATH in one session Lost on restart Use [Environment]::SetEnvironmentVariable(..., "User")
Pipelines through Out-String to parse Text-regex hell Stay in object space

Troubleshooting

Symptom Cause Fix
... is not recognized as a cmdlet Typo or not installed Get-Command <name>; check module/path
Access denied Needs elevation Run PowerShell as administrator
Scripts won't run ExecutionPolicy Get-ExecutionPolicy; Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
Output cut off Console width Select-Object fewer properties or Format-List
Invoke-RestMethod TLS error Old TLS default [Net.ServicePointManager]::SecurityProtocol = 'Tls12'
Can't find python after install PATH not refreshed New terminal; $env:Path refresh or setx

Official Documentation

Last Updated

2026-09-06 — Initial version.