PowerShell Scripting for Beginners
This article walks you through installing PowerShell 7.6 LTS, running your first cmdlets, and writing a working `.ps1` script. It is aimed at anyone who can already open a terminal and type a command, on Windows, Linux, or macOS.
Requirements
You need a machine with one of the following:- Windows 10 22H2 or Windows 11 24H2
- Ubuntu 22.04 or 24.04, Debian 12, RHEL 9, or a comparable distribution
- macOS 13 or newer
We target PowerShell 7.6, released 18 March 2026, supported until 14 November 2028. That is the current Long-Term Servicing release. If you are on Windows and only ever use the built-in `powershell.exe` (Windows PowerShell 5.1, shipped in 2016), you are running a version that will not receive new features. It still works. It is not where you should start.
Steps
1. Install PowerShell 7.6
On Windows, the cleanest path is `winget`. It handles the MSI, the PATH entry, and future upgrades.```powershell winget install --id Microsoft.PowerShell --source winget ```
On Ubuntu 24.04, install from the Microsoft repository rather than the distribution's `powershell` package, which lags behind.
```bash sudo apt update sudo apt install -y wget gpg apt-transport-https wget -q https://packages.microsoft.com/config/ubuntu/24.04/packages-microsoft-prod.deb sudo dpkg -i packages-microsoft-prod.deb sudo apt update sudo apt install -y powershell ```
On macOS with Homebrew:
```bash brew install --cask powershell ```
Why not use the version that ships with your OS? Because on Windows that is 5.1, and on Linux it may be a 7.2 or 7.3 build already out of support. You want one version across every machine you touch, so your scripts behave the same everywhere.
2. Verify the installation and check the version
Start the shell and confirm what you are running.```powershell pwsh -Version ```
You should see `PowerShell 7.6.x`. The `pwsh` command is the cross-platform binary; `powershell` on Windows still launches the old 5.1 engine. That naming split trips up almost everyone once.
3. Learn the verb-noun pattern
Every cmdlet follows the same shape: a verb, a hyphen, a noun. `Get-Process`, `Stop-Service`, `Set-Location`. This is not cosmetic. It means you can guess the name of a command you have never seen.Want to see running processes? `Get-Process`. Want to stop one? `Stop-Process`. Want to see services? `Get-Service`. The verb tells you the action, the noun tells you the target.
Find the commands available for a noun:
```powershell Get-Command -Noun Process ```
That returns every cmdlet that operates on processes. On a fresh install you will see a handful; on a server with modules loaded, dozens.
4. Use the built-in help system
PowerShell ships with help content, but on first install it often needs to be downloaded.```powershell Update-Help -Force Get-Help Get-Process -Full ```
`Get-Help` with `-Full` shows every parameter, its type, whether it is required, and an example. This is the single most useful command in the language. If you learn nothing else from this article, learn this one.
Add `-Examples` for just the examples:
```powershell Get-Help Get-Process -Examples ```
The help system is why PowerShell is friendly to beginners despite its size. You do not memorize 1,500 cmdlets. You memorize how to look them up.
5. Work with objects, not text
This is the conceptual jump. In Bash, `ls` returns lines of text you parse with `awk` or `cut`. In PowerShell, `Get-ChildItem` returns `FileInfo` objects with properties like `Name`, `Length`, and `LastWriteTime`.```powershell Get-ChildItem | Where-Object { $_.Length -gt 1MB } | Sort-Object Length -Descending | Select-Object Name, Length ```
Read that pipeline left to right: list everything in the current directory, keep only files larger than 1 MB, sort by size descending, show the name and size columns. No `awk`. No regex. No quoting hell.
The `$_` is the current object in the pipeline. It is the closest thing PowerShell has to a pronoun, and you will type it thousands of times.
If you come from Bash, this will feel wrong for about a day. Then you will try to go back and it will feel worse.
6. Create your first script
A script is a text file ending in `.ps1`. Create one called `disk-report.ps1`:```powershell param( [string]$Path = ".", [int]$MinSizeMB = 10 )
$threshold = $MinSizeMB * 1MB $files = Get-ChildItem -Path $Path -Recurse -File -ErrorAction SilentlyContinue | Where-Object { $_.Length -gt $threshold } | Sort-Object Length -Descending | Select-Object -First 20 FullName, @{Name="SizeMB"; Expression={[math]::Round($_.Length / 1MB, 2)}}
if ($files.Count -eq 0) { Write-Warning "No files larger than $MinSizeMB MB found in $Path" exit 0 }
$files | Format-Table -AutoSize Write-Output "Total files reported: $($files.Count)" ```
Three things to notice. The `param()` block at the top declares arguments, so you can call the script with `-Path` and `-MinSizeMB` like any cmdlet. The `@{Name=...; Expression=...}` syntax is a calculated property — it adds a column that did not exist on the object. And `Write-Warning` vs `Write-Output` matters: warnings go to the warning stream, output goes to the success stream, and they can be redirected separately.
Run it:
```powershell .\disk-report.ps1 -Path "C:\Users" -MinSizeMB 50 ```
On Linux or macOS:
```bash pwsh ./disk-report.ps1 -Path /var/log -MinSizeMB 5 ```
7. Handle the execution policy on Windows
By default, Windows blocks `.ps1` files from running. You will see an error like `cannot be loaded because running scripts is disabled on this system`. That is not a bug — it is a deliberate default that stops a downloaded email attachment from running silently.For your own machine, set the policy for the current user only:
```powershell Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser ```
`RemoteSigned` allows local scripts to run unsigned, but requires a signature on scripts downloaded from the internet. `Bypass` disables the check entirely and is the wrong answer outside a throwaway container. `AllSigned` is stricter and correct for production servers, but it means you must sign every script you write — a real cost, and one most beginners should not pay until they have something worth signing.
Linux and macOS do not have an execution policy. The script either has a shebang line and execute permission, or you invoke it through `pwsh`.
8. Test destructive commands with -WhatIf
Before you run a command that deletes, stops, or overwrites something, check whether it supports `-WhatIf`. Most built-in cmdlets do.```powershell Remove-Item -Path "C:\Temp\*" -Recurse -WhatIf ```
PowerShell prints what it would do and exits without touching anything. Drop `-WhatIf` when the output matches what you intended.
This is not a substitute for backups. It is a substitute for regret.
Verification
Confirm the environment works end to end by running the script with a parameter that returns results, and check the exit code.```powershell pwsh -NoProfile -Command "& ./disk-report.ps1 -Path . -MinSizeMB 1; exit $LASTEXITCODE" echo $? ```
A `0` means the script completed without an unhandled error. Anything else means something failed — a missing path, a permission problem, a syntax error in the script itself.
You can also check which version of PowerShell is actually executing the script, which matters when you have both 5.1 and 7.6 installed:
```powershell pwsh -NoProfile -Command '$PSVersionTable.PSVersion' ```
If that prints `7.6.x`, you are running the version this article targets.
Common problems
`Get-Help` returns only syntax, no descriptions. The help files are not installed. Run `Update-Help -Force`. If it fails behind a proxy, download the help cab files manually from Microsoft or use `-Source` to point at a local share.Scripts run from the terminal but fail in Task Scheduler or cron. The scheduled environment does not load your profile and may use a different PATH. Call `pwsh` with the full path and add `-NoProfile`. On Windows Task Scheduler, set the "Start in" directory explicitly — otherwise relative paths inside your script resolve against `C:\Windows\System32`.
Output looks like a wall of text with no columns. You piped an object to `Out-String` or `Write-Host` too early. Objects stay objects until they hit the formatter. Keep them in the pipeline and let `Format-Table` or `Format-List` do the rendering at the very end.
PowerShell 7.6 is supported until November 2028. That is long enough to write scripts you will still be running when the next LTS arrives — which is exactly the point of picking it now instead of waiting.