01 · Setup & First Script¶
PowerShell today means PowerShell 7+ (also called "PowerShell Core"),
built on .NET and run with the pwsh executable. It's open source and
cross-platform — the same scripts run on Windows, macOS, and Linux. This is
different from Windows PowerShell 5.1, the older, Windows-only, built-in
version (powershell.exe). Throughout this course, "PowerShell" means
PowerShell 7+ unless noted otherwise.
Installing PowerShell 7+¶
# Windows (winget)
winget install --id Microsoft.PowerShell --source winget
# macOS (Homebrew)
brew install --cask powershell
# Linux (Ubuntu/Debian) — see Microsoft's docs for your distro's exact steps
sudo apt-get install -y powershell
Verify the install:
Starting a session¶
Launch pwsh from any terminal to get an interactive session — a REPL
(Read-Eval-Print Loop) where you can type commands one at a time and see
results immediately.
Your first script file¶
Scripts live in .ps1 files. Create one with any text editor:
# hello.ps1
Write-Host "Hello, PowerShell!"
Write-Output "This line is returned as output, not just printed."
Run it:
Write-Host writes directly to the console (for humans) and cannot be
captured or piped onward. Write-Output sends an object down the pipeline —
it can be captured in a variable, piped to another cmdlet, or redirected to a
file. Prefer Write-Output (or simply letting an expression's result fall
through) for anything that might be consumed by other code; reserve
Write-Host for pure console messages like progress notes.
Execution policy¶
On Windows, PowerShell blocks running local scripts by default as a safety measure. Check and adjust it with:
Get-ExecutionPolicy
# Restricted
# Allow locally-created scripts to run for the current user only
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
RemoteSigned is the common safe default: local scripts run freely, but
scripts downloaded from the internet must be digitally signed. macOS and
Linux builds of pwsh do not enforce this policy the same way, so you'll
mostly encounter it on Windows.
Comments and basic syntax¶
# a single-line comment
<#
a block comment,
spanning multiple lines
#>
Write-Output "PowerShell statements don't need a trailing semicolon,"
Write-Output "but you can use one to put two statements on one line."; Write-Output "like this"
The help system¶
PowerShell's built-in help is one of its best features — every cmdlet is self-documenting.
Get-Help Get-Process
Get-Help Get-Process -Examples
Get-Help Get-Process -Full
# first time only, to download the latest help content
Update-Help
CommandType Name Version Source
----------- ---- ------- ------
Cmdlet Get-Process 7.0.0.0 Microsoft.PowerShell.Management
Cmdlet Stop-Process 7.0.0.0 Microsoft.PowerShell.Management
...
Cheat sheet¶
| Command | Purpose |
|---|---|
pwsh |
start an interactive PowerShell 7+ session |
pwsh ./script.ps1 |
run a script from the shell |
Get-ExecutionPolicy / Set-ExecutionPolicy |
check / change script-running permissions |
Write-Output |
send an object down the pipeline |
Write-Host |
print directly to the console (not pipeable) |
Get-Help <cmdlet> |
show documentation for a cmdlet |
Get-Command -Noun <thing> |
find cmdlets related to a noun |
How It Actually Works¶
pwsh is not "the Windows console with new commands" — it's a hosted .NET
process. When you launch it, the executable spins up a runspace: a CLR
AppDomain-hosted environment containing a session state (variables,
functions, aliases, drives) and an engine that parses and executes commands
against it. Every PowerShell host — the console, the ISE, VS Code's
terminal, a remote session — is just a different front end creating and
talking to a runspace through the same System.Management.Automation API.
When you type a line and press Enter, three distinct stages run before anything happens on screen:
- Tokenizing/parsing — the engine's parser (not a shell-style word
splitter) builds an Abstract Syntax Tree (AST) from the line, using
PowerShell's actual grammar: pipelines, statements, expressions,
command-parameter-argument triples. This is why
Get-Process | Stop-+ half a cmdlet name gives you a real parse error, not a "command not found" from a shell. - Command resolution — the parser hands each command element to the
engine's command discovery service, which searches, in order: functions
in the current session state, aliases, cmdlets registered by loaded
modules, then external executables on
PATH. This ordering is why you can define a function namedlsthat silently wins over the built-in alias. - Execution — the AST is compiled (in PowerShell 5+/7, actually
JIT-compiled via expression trees for hot code, not purely
tree-walked) and run. Cmdlets are .NET classes implementing
Cmdlet/PSCmdletwithBeginProcessing/ProcessRecord/EndProcessingmethods called by the pipeline processor — this is why every cmdlet supports the pipeline uniformly, unlike shell tools that only understand text streams.
Cross-platform pwsh (built on .NET, formerly .NET Core) achieves the same
behavior on macOS/Linux/Windows by shipping the whole engine as managed
code rather than shelling out to OS-specific APIs — the handful of
Windows-only cmdlets (like Get-WmiObject) are the exceptions that rely on
Windows-specific COM/WMI interop unavailable on other platforms.
🔀 See this in another language¶
Exercise¶
Write greet.ps1 that uses Write-Output to print a greeting, then calls
Get-Date and stores the result in a variable, then prints a second message
that includes that variable so it reads something like
"Generated on <date>.". Run it with pwsh ./greet.ps1 and confirm both
lines appear.