CMD vs PowerShell vs Bash — Command Equivalents

The same task in Command Prompt, PowerShell and bash, side by side: files, grep, environment variables, processes, network, services and scripting.

CMD vs PowerShell vs Bash · freetoolsforweb.com/tools/cmd-vs-powershell-vs-bash · verified 2026-10-18

Last verified: 2026-10-18 against the Microsoft Learn PowerShell 7.6 and Windows PowerShell 5.1 reference, the Windows Commands reference and the GNU Bash 5.2 manual.

107 tasks in 9 groups, plus alias, quoting and policy notes

Files & folders

TaskCMD (Command Prompt)PowerShellBash (Linux/macOS)
List files #
PowerShell aliases: gci, dir, and on Windows ls.
dir
Get-ChildItem
ls -l
List including hidden files #
dir /a
Get-ChildItem -Force
ls -la
List all files recursively #
dir /s /b
Get-ChildItem -Recurse -File
find . -type f
Show current directory #
In CMD, cd with no argument prints the current directory. PowerShell alias: pwd, gl; also the $PWD variable.
cd
Get-Location
pwd
Change directory #
CMD needs /d to switch drive as well as folder. PowerShell aliases: cd, sl, chdir.
cd /d D:\work
Set-Location D:\work
cd /home/me/work
Go to home directory #
cd %USERPROFILE%
Set-Location ~
cd ~
Create a folder (with parents) #
CMD mkdir creates intermediate folders when command extensions are on (the default). In PowerShell, mkdir is a function that wraps New-Item -ItemType Directory.
mkdir a\b\c
New-Item -ItemType Directory -Path a\b\c
mkdir -p a/b/c
Create an empty file #
type nul > file.txt
New-Item file.txt
touch file.txt
Copy a file #
copy src.txt dst.txt
Copy-Item src.txt dst.txt
cp src.txt dst.txt
Copy a folder recursively #
xcopy src dst /E /I also works; robocopy is the supported tool and can resume and retry.
robocopy src dst /E
Copy-Item src dst -Recurse
cp -r src dst
Mirror a folder exactly Dangerous #
Danger: Both delete files in the destination that are not in the source. Swapping the two paths wipes the folder you meant to keep. robocopy /L and rsync -n do a dry run.
robocopy src dst /MIR
(no cmdlet; call robocopy)
rsync -a --delete src/ dst/
Move a file #
move a.txt archive\
Move-Item a.txt archive\
mv a.txt archive/
Rename a file #
ren old.txt new.txt
Rename-Item old.txt new.txt
mv old.txt new.txt
Delete a file #
del file.txt
Remove-Item file.txt
rm file.txt
Delete a folder and its contents Dangerous #
Danger: No Recycle Bin and no prompt with /q, -Force or -f. Check the path first; in PowerShell add -WhatIf to see what would be removed.
rmdir /s /q folder
Remove-Item folder -Recurse -Force
rm -rf folder
Change file permissions #
Windows uses ACLs, not rwx bits; icacls is usually easier than Set-Acl.
icacls file.txt /grant alice:F
Get-Acl / Set-Acl
chmod 644 file.txt
File checksum (SHA-256) #
Get-FileHash uses SHA256 by default; -Algorithm MD5 or SHA1 for others. macOS: shasum -a 256.
certutil -hashfile file.iso SHA256
Get-FileHash file.iso
sha256sum file.iso
Create a zip file #
tar.exe (bsdtar) ships with Windows 10 1803 and later; -a picks the format from the .zip extension.
tar -a -c -f out.zip folder
Compress-Archive -Path folder -DestinationPath out.zip
zip -r out.zip folder
Extract a zip file #
tar -xf out.zip
Expand-Archive out.zip -DestinationPath dest
unzip out.zip -d dest

Reading & writing file contents

TaskCMD (Command Prompt)PowerShellBash (Linux/macOS)
Print a file #
PowerShell aliases: gc, type, and on Windows cat.
type file.txt
Get-Content file.txt
cat file.txt
Page through a file #
more file.txt
Get-Content file.txt | Out-Host -Paging
less file.txt
Last 10 lines #No built-in command
Get-Content file.txt -Tail 10
tail -n 10 file.txt
Follow a log file #No built-in command
Get-Content app.log -Tail 10 -Wait
tail -f app.log
Write text to a file (overwrite) #
In CMD, a space before > ends up in the file. In Windows PowerShell 5.1, > writes UTF-16 LE; PowerShell 7 writes UTF-8 without BOM. Set-Content -Encoding utf8 is explicit in both.
echo hello> file.txt
'hello' | Set-Content file.txt
echo hello > file.txt
Append text to a file #
echo hello>> file.txt
Add-Content file.txt 'hello'
echo hello >> file.txt
Count lines #
find /c /v "" file.txt
(Get-Content file.txt).Count
wc -l file.txt
Compare two files #
In PowerShell on Windows, diff is an alias for Compare-Object, not a text diff.
fc a.txt b.txt
Compare-Object (Get-Content a.txt) (Get-Content b.txt)
diff a.txt b.txt
Sort lines #
sort file.txt
Get-Content file.txt | Sort-Object
sort file.txt
Unique lines #No built-in command
Get-Content file.txt | Sort-Object -Unique
sort -u file.txt
Replace text in a file #
-replace uses regular expressions; the parentheses read the whole file before Set-Content reopens it. macOS sed needs -i ''.
No built-in command
(Get-Content f.txt) -replace 'old','new' | Set-Content f.txt
sed -i 's/old/new/g' f.txt

Environment variables

TaskCMD (Command Prompt)PowerShellBash (Linux/macOS)
List all variables #
set
Get-ChildItem Env:
env
Read a variable #
echo %PATH%
$env:PATH
echo "$PATH"
Set for this session #
CMD: no spaces around =, or they become part of the name and value.
set MY_VAR=hello
$env:MY_VAR = 'hello'
export MY_VAR=hello
Set permanently (for your user) #
setx and SetEnvironmentVariable affect new windows only, not the current one. setx truncates values longer than 1024 characters, so do not use it to edit PATH.
setx MY_VAR hello
[Environment]::SetEnvironmentVariable('MY_VAR','hello','User')
echo 'export MY_VAR=hello' >> ~/.bashrc
Remove a variable #
set MY_VAR=
Remove-Item Env:MY_VAR
unset MY_VAR
Add a folder to PATH (session) #
PowerShell 7 on Linux/macOS separates PATH with : instead of ;. [IO.Path]::PathSeparator gives the right one.
set PATH=%PATH%;C:\tools
$env:PATH += ';C:\tools'
export PATH="$PATH:/opt/tools"
Set only for one command #No built-in command
$env:DEBUG = '1'; ./app; Remove-Item Env:DEBUG
DEBUG=1 ./app

Processes

TaskCMD (Command Prompt)PowerShellBash (Linux/macOS)
List processes #
tasklist
Get-Process
ps aux
Find a process by name #
tasklist /FI "IMAGENAME eq chrome.exe"
Get-Process chrome
pgrep -a chrome
Kill by process ID #
Without /F or -Force, Windows asks the process to close. kill sends SIGTERM; kill -9 forces.
taskkill /PID 1234 /F
Stop-Process -Id 1234 -Force
kill 1234
Kill by name #
taskkill /IM notepad.exe /F
Stop-Process -Name notepad
pkill notepad
Start a program #
The empty "" in start is the window title; without it, a quoted program path is taken as the title.
start "" notepad.exe
Start-Process notepad
gedit &
Run elevated (as admin/root) #
Use powershell instead of pwsh for Windows PowerShell 5.1. Windows 11 24H2 and later can also enable a built-in sudo command in Settings.
runas /user:Administrator cmd
Start-Process pwsh -Verb RunAs
sudo command
Top 10 processes by CPU #
CPU in Get-Process is total processor seconds since start, not current %.
No built-in command
Get-Process | Sort-Object CPU -Descending | Select-Object -First 10
ps aux --sort=-%cpu | head
What is using a port #
Get-NetTCPConnection is Windows-only. The last column of netstat -ano is the PID: look it up with tasklist /FI "PID eq 1234".
netstat -ano | findstr :8080
Get-NetTCPConnection -LocalPort 8080
sudo ss -ltnp | grep :8080

Network

TaskCMD (Command Prompt)PowerShellBash (Linux/macOS)
Show IP addresses #
Get-NetIPAddress and the other Net* cmdlets are Windows-only (NetTCPIP module).
ipconfig /all
Get-NetIPAddress
ip addr
Ping a host #
Windows ping stops after 4 by default; Linux ping runs until Ctrl+C.
ping -n 4 example.com
Test-Connection example.com -Count 4
ping -c 4 example.com
Test a TCP port #
Test-NetConnection is Windows-only. In PowerShell 7: Test-Connection example.com -TcpPort 443.
No built-in command
Test-NetConnection example.com -Port 443
nc -zv example.com 443
DNS lookup #
Resolve-DnsName is Windows-only; nslookup exists on all three.
nslookup example.com
Resolve-DnsName example.com
dig example.com
Trace route #
tracert example.com
Test-NetConnection example.com -TraceRoute
traceroute example.com
Flush DNS cache #
The bash line applies to systemd-resolved systems.
ipconfig /flushdns
Clear-DnsClientCache
sudo resolvectl flush-caches
HTTP GET (API call) #
Invoke-RestMethod parses JSON into objects; Invoke-WebRequest returns status, headers and raw content.
curl.exe https://api.example.com/items
Invoke-RestMethod https://api.example.com/items
curl https://api.example.com/items
Download a file #
curl.exe ships with Windows 10 1803 and later. In Windows PowerShell 5.1, add -UseBasicParsing to Invoke-WebRequest on machines without Internet Explorer set up.
curl.exe -L -o file.zip https://example.com/file.zip
Invoke-WebRequest https://example.com/file.zip -OutFile file.zip
curl -LO https://example.com/file.zip
POST JSON #
CMD has no single quotes, so inner double quotes are escaped with backslashes for curl.exe.
curl.exe -X POST -H "Content-Type: application/json" -d "{\"a\":1}" https://api.example.com
Invoke-RestMethod https://api.example.com -Method Post -ContentType 'application/json' -Body (@{a=1} | ConvertTo-Json)
curl -X POST -H 'Content-Type: application/json' -d '{"a":1}' https://api.example.com
SSH to a server #
The OpenSSH client is installed by default on Windows 10 1809+ and Windows 11.
ssh user@host
ssh user@host
ssh user@host

Services & system

TaskCMD (Command Prompt)PowerShellBash (Linux/macOS)
List services #
In Windows PowerShell 5.1, sc is an alias for Set-Content: type sc.exe there. PowerShell 6+ removed that alias.
sc query
Get-Service
systemctl list-units --type=service
Service status #
sc query Spooler
Get-Service Spooler
systemctl status nginx
Start / stop a service #
net start Spooler net stop Spooler
Start-Service Spooler Stop-Service Spooler
sudo systemctl start nginx sudo systemctl stop nginx
Restart a service #
net stop Spooler && net start Spooler
Restart-Service Spooler
sudo systemctl restart nginx
Start at boot #
sc.exe needs the space after start= and no space before it.
sc config Spooler start= auto
Set-Service Spooler -StartupType Automatic
sudo systemctl enable nginx
OS and hardware info #
systeminfo
Get-ComputerInfo
uname -a; cat /etc/os-release
Computer name #
hostname
$env:COMPUTERNAME
hostname
Current user #
Also $env:USERNAME in PowerShell on Windows.
whoami
whoami
whoami
Free disk space #
Older Windows versions require an elevated prompt for fsutil. Get-Volume (Windows-only) also shows size and free space.
fsutil volume diskfree C:
Get-PSDrive -PSProvider FileSystem
df -h
Uptime #
Get-Uptime is PowerShell 6+. In 5.1: (Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime.
systeminfo | find "Boot Time"
Get-Uptime
uptime
Reboot now #
shutdown /r /t 0
Restart-Computer
sudo reboot
Clear the screen #
cls and clear both work in PowerShell on Windows.
cls
Clear-Host
clear
Command history #
Get-History only covers this session. PSReadLine keeps older history in the file at (Get-PSReadLineOption).HistorySavePath; Ctrl+R searches it in all three shells.
doskey /history
Get-History
history
Help for a command #
Run Update-Help once (elevated in 5.1) to download full PowerShell help.
dir /?
Get-Help Get-ChildItem -Examples
man ls
Current date #
%date% follows the regional format of the machine, so scripts that parse it break across locales.
echo %date%
Get-Date -Format 'yyyy-MM-dd'
date +%F
Wait 5 seconds #
timeout /t 5
Start-Sleep -Seconds 5
sleep 5
Copy output to clipboard #
macOS: pbcopy. Wayland: wl-copy. Get-Clipboard reads it back in PowerShell.
dir | clip
Get-ChildItem -Name | Set-Clipboard
ls | xclip -selection clipboard

Piping objects vs text

TaskCMD (Command Prompt)PowerShellBash (Linux/macOS)
Pick columns / properties #
PowerShell pipes .NET objects, so you choose properties by name instead of parsing columns.
No built-in command
Get-Process | Select-Object Name, Id, WS
ps -eo comm,pid,rss
Filter rows by a value #
PowerShell understands 500MB, 1GB literals. awk compares column 6 (RSS in KB).
tasklist | findstr chrome
Get-Process | Where-Object WS -gt 500MB
ps aux | awk '$6 > 500000'
Do something per item #
$_ (or $PSItem) is the current object. In a .bat file write %%f instead of %f.
for %f in (*.txt) do echo %f
Get-ChildItem *.txt | ForEach-Object { $_.Name }
for f in *.txt; do echo "$f"; done
See what properties an object has #
The single most useful PowerShell discovery command; also try | Format-List *.
No built-in command
Get-Process | Get-Member
No built-in command
Parse JSON #
Use -Raw so the file arrives as one string; ConvertTo-Json -Depth 10 for output (the default depth is 2).
No built-in command
Get-Content data.json -Raw | ConvertFrom-Json
jq . data.json
Read and filter a CSV #
Import-Csv values are strings; cast with [int] for numeric comparisons.
No built-in command
Import-Csv users.csv | Where-Object { [int]$_.Age -gt 30 }
awk -F, 'NR>1 && $3 > 30' users.csv
Export to CSV #
-NoTypeInformation is the default in PowerShell 6+ but needed in 5.1.
No built-in command
Get-Process | Export-Csv procs.csv -NoTypeInformation
ps -eo pid,comm --no-headers | awk '{print $1","$2}' > procs.csv
Count, sum, average #
find /c /v "" file.txt
Get-ChildItem -File | Measure-Object Length -Sum -Average
ls -l | awk '{s+=$5} END {print s}'

Scripting: variables, conditionals, loops, functions

TaskCMD (Command Prompt)PowerShellBash (Linux/macOS)
Set a variable #
bash: no spaces around = (name = Ada runs a command called name).
set name=Ada
$name = 'Ada'
name=Ada
Use a variable in a string #
echo Hello %name%
"Hello $name"
echo "Hello $name"
Arithmetic #
CMD and bash do integer math only; PowerShell uses .NET numbers.
set /a n=5*3
$n = 5 * 3
n=$((5 * 3))
Read user input #
set /p name=Name:
$name = Read-Host 'Name'
read -rp 'Name: ' name
Script arguments #
In .bat files %~1 strips surrounding quotes. A param() block gives PowerShell scripts named, typed parameters.
%1 %2 … %*
$args[0] or param($Path)
$1 $2 … "$@"
If / else on a string #
PowerShell comparison operators are words: -eq -ne -gt -lt -like -match. = and > are assignment and redirection.
if "%a%"=="yes" (echo on) else (echo off)
if ($a -eq 'yes') { 'on' } else { 'off' }
if [ "$a" = "yes" ]; then echo on; else echo off; fi
Compare numbers #
CMD: EQU NEQ LSS LEQ GTR GEQ.
if %n% GTR 5 echo big
if ($n -gt 5) { 'big' }
if (( n > 5 )); then echo big; fi
If a file exists #
if exist file.txt echo yes
if (Test-Path file.txt) { 'yes' }
if [ -f file.txt ]; then echo yes; fi
Exit code of the last program #
In PowerShell, $? is $true/$false for the last command; $LASTEXITCODE is the numeric code of the last native program.
echo %ERRORLEVEL%
$LASTEXITCODE
echo $?
Run next command only on success / failure #
&& and || pipeline chain operators arrived in PowerShell 7.0; Windows PowerShell 5.1 rejects them.
build && deploy build || echo failed
build && deploy (PowerShell 7+) build; if ($?) { deploy } (5.1)
build && deploy build || echo failed
Loop over a list #
for %i in (a b c) do echo %i
foreach ($x in 'a','b','c') { $x }
for x in a b c; do echo "$x"; done
Loop 1 to 5 #
for /l %i in (1,1,5) do echo %i
foreach ($i in 1..5) { $i }
for i in {1..5}; do echo "$i"; done
Loop over lines of a file #
for /f "usebackq delims=" %l in ("file.txt") do echo %l
foreach ($line in Get-Content file.txt) { $line }
while IFS= read -r line; do echo "$line"; done < file.txt
While loop #
CMD has no while; use goto with a label in a .bat file.
:loop set /a i+=1 if %i% lss 5 goto loop
while ($i -lt 5) { $i++ }
while [ "$i" -lt 5 ]; do i=$((i+1)); done
Define and call a function #
Call PowerShell functions like commands: Greet Ada, not Greet("Ada").
call :greet Ada goto :eof :greet echo Hello %~1 exit /b
function Greet($Name) { "Hello $Name" } Greet Ada
greet() { echo "Hello $1"; } greet Ada
Store command output in a variable #
for /f "delims=" %i in ('hostname') do set h=%i
$h = hostname
h=$(hostname)
Discard output #
command >nul 2>&1
command *> $null
command >/dev/null 2>&1
Comment #
rem comment :: comment
# comment <# block #>
# comment
Script file and how to run it #
PowerShell does not run scripts from the current folder without .\ and may block them entirely: see execution policy below.
script.bat
.\script.ps1
chmod +x script.sh && ./script.sh

PowerShell alias gotchas: ls, curl, wget, sc, where

PowerShell ships aliases that look like Unix or CMD commands but run cmdlets with different parameters. Which ones exist depends on the edition and the operating system. Get-Alias name shows what a name points to in your session.

You typeWindows PowerShell 5.1PowerShell 7 on WindowsPowerShell 7 on Linux/macOS
curl
Type curl.exe in 5.1 to get real curl. Its flags (-X, -H, -d) mean nothing to Invoke-WebRequest.
Invoke-WebRequestnot an alias: runs curl.exenative curl
wget
Use Invoke-WebRequest url -OutFile name, or curl.exe -LO url.
Invoke-WebRequestnot an alias (no wget on Windows by default)native wget, if installed
ls, dir
Flags like ls -la fail on Windows: use Get-ChildItem -Force.
Get-ChildItemGet-ChildItemls = native ls; dir = Get-ChildItem
cat, typeGet-ContentGet-Contentcat = native cat; type = Get-Content
cp, mv, rm, rmdir
rm -rf is not valid PowerShell: use Remove-Item -Recurse -Force.
Copy-Item, Move-Item, Remove-Itemsame cmdletsnative commands
ps, killGet-Process, Stop-Processsame cmdletsnative commands
sort, tee, diff
diff a b in PowerShell on Windows compares objects, not lines of two files.
Sort-Object, Tee-Object, Compare-Objectsame cmdletsnative commands
sc
In 5.1, sc query writes a file named query. Use sc.exe.
Set-Contentnot an alias: runs sc.exen/a
where
Use where.exe to find a program, or Get-Command.
Where-ObjectWhere-ObjectWhere-Object
echo
echo -n does not suppress the newline; use Write-Host -NoNewline.
Write-OutputWrite-OutputWrite-Output
manhelphelpnative man

Quoting & escaping

Most “works in one shell, fails in another” bugs are quoting. The escape character is different in each shell.

WhatCMDPowerShellBash
Literal string (no expansion)No literal quotes: %VAR% expands even inside "…"'Cost: $5''Cost: $5'
String with variables"Hello %name%""Hello $name, it is $(Get-Date -Format t)""Hello $name, it is $(date +%R)"
Escape character^ (e.g. ^& ^| ^>)` backtick (e.g. `n newline, `t tab, `$ literal $)\ backslash
Quote inside a quoted string"" inside "…" for many programs; varies by program'It''s' or "Say `"hi`""'It'\''s' or "Say \"hi\""
Line continuation^ at end of line` at end of line (or end a line with | )\ at end of line
Literal % or $ in a script%% in a .bat file'$' in single quotes or `$'$' in single quotes or \$
Pass arguments untouched to a native program(not needed)--% stops PowerShell parsing the rest of the line(not needed)

Passing quotes to native programs (curl.exe, git, node) changed in PowerShell 7.3: arguments with embedded double quotes are now passed as written, controlled by $PSNativeCommandArgumentPassing. In Windows PowerShell 5.1 the same JSON body often needs its inner quotes escaped twice, which is why many people switch to Invoke-RestMethod with ConvertTo-Json.

Execution policy: “running scripts is disabled on this system”

PowerShell refuses to run .ps1 files when the execution policy is Restricted, which is the effective default for Windows PowerShell on Windows client editions (Windows 10 and 11). Windows Server defaults to RemoteSigned: local scripts run, downloaded ones must be signed or unblocked. On Linux and macOS the policy is Unrestricted and cannot be changed.

  • Get-ExecutionPolicy -List shows the policy at every scope; the most specific defined scope wins (Group Policy overrides all).
  • Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser lets your own scripts run without admin rights.
  • Unblock-File .\script.ps1 clears the “downloaded from the internet” mark on a script you trust.
  • pwsh -ExecutionPolicy Bypass -File script.ps1 (or powershell for 5.1) applies a policy to one session only.

Execution policy is a safety catch against running scripts by accident, not a security boundary: anyone can paste the script’s contents into the console.

Windows PowerShell 5.1 vs PowerShell 7

  • Two programs: powershell.exe is Windows PowerShell 5.1, built into Windows and no longer getting new features. pwsh is PowerShell 7, installed separately, cross-platform, and they can sit side by side.
  • Operators added in 7.0: && and || between commands, the ternary $a ? 'yes' : 'no', and null-coalescing ?? / ??=.
  • File encoding: 5.1’s > and Out-File write UTF-16 LE, and Set-Content uses the ANSI code page. PowerShell 7 writes UTF-8 without a BOM everywhere, which is what Git, Linux tools and most editors expect.
  • Aliases: 7 dropped curl, wget and sc on Windows, and on Linux/macOS it leaves ls, cp, rm, cat, ps and others to the native commands (table above).
  • Module gaps: Windows-only cmdlets such as Get-NetIPAddress, Test-NetConnection and Resolve-DnsName work in PowerShell 7 on Windows but do not exist on Linux or macOS.

Running bash on Windows: WSL

Windows Subsystem for Linux runs a real Linux distribution next to Windows, so every bash command in the right-hand column works as written. wsl --install from an elevated prompt installs it with Ubuntu by default (Windows 10 version 2004 and later, and Windows 11); wsl -l -v lists installed distributions.

  • Run one Linux command from CMD or PowerShell: wsl ls -la, or pipe across: dir | wsl grep txt.
  • Windows drives appear inside Linux under /mnt/c; Linux files are reachable from Explorer at \\wsl.localhost\Ubuntu (older builds: \\wsl$\Ubuntu).
  • Keep projects inside the Linux filesystem (~/), not /mnt/c: file access across the boundary is much slower.

For the Linux side in depth, see the Linux commands cheat sheet.

A static reference that runs in your browser; your searches are not sent anywhere. Browse all free tools.

Found a problem, or something that could be better? We read every message and we fix things quickly.

This CMD vs PowerShell vs Bash table shows the same task in Windows Command Prompt, PowerShell and bash on one row: listing and copying files, finding text in files (the PowerShell equivalent of grep is Select-String), environment variables, processes, networking, services, reading and writing file contents, and scripting with variables, conditionals, loops and functions. Search with a command from any of the three shells and you get its counterparts, each with a copy button.

The sheet also covers what usually goes wrong when you move between them: PowerShell aliases that look like Unix commands but are not (curl and wget in Windows PowerShell 5.1, ls and rm everywhere on Windows), different quoting and escape characters, the execution policy error that blocks .ps1 scripts, and the differences between Windows PowerShell 5.1 and PowerShell 7. PowerShell commands work in both versions unless a note says otherwise; where a CMD equivalent does not exist, the table says so rather than inventing one.

How to use

  1. Search for the command you know, in any shell (for example “grep”, “findstr” or “Select-String”), or for the task (“kill process”, “environment variable”).
  2. Read across the row for the equivalent in the other two shells and click Copy next to the one you need.
  3. Check the note under the task name: it flags version differences (PowerShell 5.1 vs 7) and Windows-only cmdlets.
  4. Scroll to the alias, quoting and execution policy sections if a command behaves differently from what you expected.
  5. Use Print / Save as PDF for a desk copy; the page also works on a phone, where each row becomes a card.

Frequently asked questions

What is the PowerShell equivalent of grep?

Select-String. Search a file with Select-String -Path app.log -Pattern error, or filter command output with ipconfig | Select-String IPv4. It is case-insensitive by default (add -CaseSensitive), uses .NET regular expressions, and -NotMatch is the equivalent of grep -v. For recursive searches pipe Get-ChildItem -Recurse -Filter *.log into Select-String. Its short alias is sls.

Why does curl behave strangely in PowerShell?

In Windows PowerShell 5.1, curl and wget are aliases for Invoke-WebRequest, so curl flags like -X, -H and -d are rejected or mean something else. Type curl.exe to run the real curl that ships with Windows 10 1803 and later. PowerShell 7 removed those aliases, so curl runs curl.exe there. On Linux and macOS, curl was never an alias.

Is PowerShell better than CMD?

For anything beyond running a program or a short batch file, yes. PowerShell pipes structured objects instead of text, so you filter and sort by property name (Where-Object, Sort-Object) instead of parsing columns, and it has real functions, error handling, JSON and CSV support and remoting. CMD is still fine for legacy .bat files and some installers, and every CMD tool (ipconfig, robocopy, netstat) can be run from PowerShell as well.

How do I fix “running scripts is disabled on this system”?

That is the execution policy, which is Restricted by default on Windows 10 and 11 client editions. Run Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser in PowerShell (no admin needed). Your own scripts then run, and scripts downloaded from the internet must be signed or unblocked with Unblock-File. Get-ExecutionPolicy -List shows what is set where; a Group Policy setting overrides anything you set yourself.

Can I run bash commands on Windows?

Yes, with Windows Subsystem for Linux. Run wsl --install from an elevated prompt (Windows 10 version 2004 or later, or Windows 11) to install Ubuntu, then open it from the Start menu or run single commands from CMD or PowerShell with wsl followed by the command, such as wsl grep -rn TODO. Git for Windows also includes Git Bash, a lighter bash with the common Unix tools.

What is the difference between Windows PowerShell and PowerShell 7?

Windows PowerShell 5.1 (powershell.exe) is built into Windows and only receives fixes. PowerShell 7 (pwsh) is the current, cross-platform version that runs on Windows, Linux and macOS and is installed separately; both can be installed together. Version 7 adds && and || between commands, the ternary and ?? operators, writes UTF-8 files by default and drops some confusing aliases such as curl and wget.

Objects vs text: the one idea that makes PowerShell click

In CMD and bash a pipe carries text. To get the process using the most memory in bash you pick the right column out of ps output with sort -k or awk, and the command breaks if the column layout changes. In PowerShell the pipe carries objects with named properties, so the same job is Get-Process | Sort-Object WS -Descending | Select-Object -First 1. Nothing is parsed, and the property names are the same on every machine.

The practical consequence is a different discovery workflow. In bash you read a man page to learn the output format; in PowerShell you pipe any command into Get-Member to see which properties and methods its objects have, then filter with Where-Object and choose fields with Select-Object. Only when the output reaches the screen or a file does PowerShell turn it into text.

Native programs like ipconfig, git or curl.exe still output text in PowerShell, so Select-String and -match work on them the way grep would. Their exit code is in $LASTEXITCODE, not $?.

Translating a script between the three shells

Line-by-line translation works for simple scripts, but a few differences catch almost everyone:

• Comparison operators: CMD uses == for strings and EQU, GTR, LSS for numbers; PowerShell uses -eq, -gt, -lt, -like and -match (a bare > is redirection); bash uses = inside [ ] for strings and -gt or (( )) for numbers.

• Variables: %name% in CMD (and !name! inside loops after setlocal EnableDelayedExpansion), $name in PowerShell, $name in bash with no spaces around = when assigning.

• Loops in .bat files double the percent sign: for %%f in (*.txt) inside a file, for %f at the prompt.

• Paths: PowerShell accepts both / and \ on Windows; bash on Linux needs /. Environment variables are case-insensitive on Windows and case-sensitive on Linux.

• Errors: CMD continues after a failure unless you check %ERRORLEVEL%; bash continues unless set -e; PowerShell continues after non-terminating errors unless $ErrorActionPreference = 'Stop'.

When a script needs to run on Windows and Linux, PowerShell 7 or bash under WSL avoids keeping two copies.

Related free tools

  • Linux Commands Cheat Sheet — Linux commands by task, from files and permissions to networking, systemd and text processing. Every entry has flags, copyable examples and macOS notes.
  • Windows Keyboard Shortcuts — Every Windows 11 shortcut in one searchable, printable list: Win key, Snap, desktops, screenshots, File Explorer.
  • Mac Keyboard Shortcuts — Every macOS shortcut with ⌘ ⌥ ⌃ ⇧ symbols explained: screenshots, window tiling, Finder, Safari and special characters.
  • curl to Code — Paste a curl command, including Chrome "Copy as cURL", and get the same request in Python, JavaScript, Node, Go, PHP, Ruby, Java, C#, wget or HTTPie.
  • Git Cheat Sheet — Git commands organised by what you want to do: undo the last commit, rename a branch, delete a remote branch. Modern switch/restore with classic equivalents and danger badges.