PowerShell pipes objects, not text. That single difference is why ls | grep foo | awk {print $2} has no direct translation — you select a property instead of cutting a column, and nothing needs parsing. This reference covers PowerShell 7.6.5 (released 14 August 2026, the current LTS line), which runs on Windows, macOS, and Linux — not the Windows PowerShell 5.1 that ships in the box on Windows and is no longer getting features.
When to reach for this
- You know the bash equivalent and need the cmdlet — filtering, sorting, and selecting fields off a command's output.
- A script fails with
running scripts is disabled on this systemand you need the execution-policy fix. - You are setting environment variables on Windows and cannot remember which of
$env:,setx, or[Environment]::SetEnvironmentVariablepersists.
Which Version Are You Actually Running?
| Aspect | Windows PowerShell 5.1 | PowerShell 7.x |
|---|---|---|
| Executable | powershell.exe | pwsh.exe / pwsh |
| Runtime | .NET Framework 4.x | .NET (cross-platform) |
| Platforms | Windows only | Windows, macOS, Linux |
| Status | Maintenance — no new features | Actively developed |
| Installed by default | Yes, on Windows | No — install separately |
Check with $PSVersionTable.PSVersion. Both can be installed side by side, and they are separate shells with separate profiles and separate module paths — a module you installed in one will not be visible in the other. If a script only works in one of them, this is the first thing to check.
What Are the Core Cmdlets?
Cmdlets are Verb-Noun. Once you know the verbs — Get, Set, New, Remove, Start, Stop, Test — most names are guessable, and Get-Command *service* finds the rest.
| Cmdlet | Description | Unix equivalent |
|---|---|---|
| Get-ChildItem | List directory contents | ls |
| Set-Location | Change directory | cd |
| Get-Content | Read a file | cat |
| Set-Content | Write a file, overwriting | tee / > |
| Add-Content | Append to a file | >> |
| Copy-Item | Copy a file or directory | cp |
| Move-Item | Move or rename | mv |
| Remove-Item | Delete | rm |
| New-Item | Create a file or directory | touch / mkdir |
| Select-String | Search text with regex | grep |
| Get-Process | List running processes | ps |
| Stop-Process | Kill a process | kill |
| Get-Command | Find a cmdlet or executable | which / type |
| Get-Help | Documentation for a cmdlet | man |
| Measure-Object | Count, sum, average a property | wc |
How Do You Filter and Shape the Pipeline?
| Command | Description |
|---|---|
| Where-Object { $_.Size -gt 1MB } | Filter objects by a condition |
| Where-Object Name -like "*.log" | Simplified syntax for a single comparison |
| Select-Object Name, Length | Keep only these properties |
| Select-Object -First 10 | Take the first ten objects |
| Select-Object -ExpandProperty Name | Unwrap to the raw values, not objects |
| Sort-Object Length -Descending | Sort by a property |
| Group-Object Extension | Group and count by a property |
| ForEach-Object { $_.ToUpper() } | Run a block per object |
| Measure-Object Length -Sum | Aggregate a numeric property |
| Get-Member | Show every property and method on an object |
Get-Member is the one to internalise. Pipe anything into it and you get the full list of properties you can select or filter on — it replaces guessing at column positions entirely. The automatic variable $_ (or its clearer alias $PSItem) is the current pipeline object.
Comparison Operators
PowerShell does not use > and < for comparison — those are redirection. Operators are words prefixed with a hyphen, and they are case-insensitive by default.
| Operator | Meaning |
|---|---|
| -eq / -ne | Equal / not equal |
| -gt / -ge | Greater than / greater or equal |
| -lt / -le | Less than / less or equal |
| -like / -notlike | Wildcard match (* and ?) |
| -match / -notmatch | Regex match; sets $Matches |
| -contains / -in | Collection membership |
| -ceq / -clike / -cmatch | Case-sensitive variants |
| -and / -or / -not | Boolean logic |
How Do You Work With Environment Variables?
| Command | Scope |
|---|---|
| $env:PATH | Read a variable in the current session |
| $env:API_KEY = "abc" | Set for this session only — lost on close |
| Get-ChildItem Env: | List every environment variable |
| Remove-Item Env:API_KEY | Unset for this session |
| [Environment]::SetEnvironmentVariable("K","v","User") | Persist for the current user |
| [Environment]::SetEnvironmentVariable("K","v","Machine") | Persist system-wide; needs admin |
| setx K v | Persist for the user; does not affect the current session |
The trap is that setx and the [Environment] machine/user scopes write to the registry and only take effect in new processes — the shell you typed them in still has the old value. That is the single most common "I set it and it did not work" report on Windows. The Windows environment variables guide covers the session-versus-persistent split, the 1024-character setx truncation, and the GUI paths in full.
How Do You Fix "Running Scripts Is Disabled"?
A fresh Windows install refuses to run .ps1 files. The error is File ... cannot be loaded because running scripts is disabled on this system.
| Command | Effect |
|---|---|
| Get-ExecutionPolicy -List | Show the policy at every scope |
| Set-ExecutionPolicy RemoteSigned -Scope CurrentUser | The usual fix — no admin needed |
| Set-ExecutionPolicy Bypass -Scope Process | Allow everything for this session only |
| pwsh -ExecutionPolicy Bypass -File .\s.ps1 | Bypass for a single invocation |
| Unblock-File .\s.ps1 | Clear the mark-of-the-web on a downloaded script |
RemoteSigned at CurrentUser scope is the right default: local scripts run, downloaded ones need a signature. Execution policy is not a security boundary — it stops accidents, not attackers, and Microsoft documents it as such.
What Are the Common Gotchas?
| Symptom | Cause | Fix |
|---|---|---|
| A variable set with setx is not visible | Registry-scoped writes only apply to new processes | Set $env:VAR too, or restart the shell |
| Output is truncated with a trailing ellipsis | The default table formatter drops columns that do not fit | Pipe to Format-List, or Select-Object the properties you need |
| A path with spaces fails | The argument was not quoted, or & is needed to invoke it | Use & "C:\Program Files\app.exe" |
| Comparing to $null the wrong way round | $x -eq $null on an array filters the array instead of testing it | Put $null on the left: $null -eq $x |
| Redirect produces UTF-16 with a BOM | Windows PowerShell 5.1 defaults to Unicode for > | Use Set-Content -Encoding utf8, or move to PowerShell 7 where UTF-8 is the default |
| A function returns more than expected | Every uncaptured expression is added to the output | Assign to $null or pipe to Out-Null to discard |
For the Unix side of the same tasks, see the Bash scripting cheat sheet; if you are running containers on Windows, the Docker on Windows guide covers the WSL2 side.