$ Shell Basics

Claude Code requires a shell to run. If you've never used one before, this guide will get you up to speed. Don't let the terminal stop you — it's simpler than it looks.

What is a shell?

A shell is a program that sits between you and the operating system. You type commands, the shell interprets them, and the OS does the work. That's it.

You → Terminal → Shell → Kernel → Hardware

You type into the terminal (the window). The terminal passes your keystrokes to the shell (bash, zsh, PowerShell). The shell interprets your commands and talks to the kernel — the core of the OS that manages memory, processes, and devices. A graphical desktop does the same thing, but with clicks instead of text.

Why bother?

Which shell?

ShellWhereNotes
bashLinux (default), macOS (older), WSLThe universal standard. Learn this first.
zshmacOS (default since Catalina)99% bash-compatible, better tab completion
PowerShellWindows (default)Object-oriented, different syntax. Covered in the comparison tab.
cmd.exeWindows (legacy)Ancient. Avoid it for anything beyond cd and dir.

Opening a terminal

Before you can type commands, you need to open a terminal — the window that runs your shell. The terminal is the UI; the shell is the language running inside it.

macOS
The built-in terminal:
  Open Applications → Utilities → Terminal

Better option — install Hyper:
  hyper.is
  Cross-platform, extensible, looks great.

The shell inside is zsh (macOS default).
It works almost identically to bash.

You'll see a prompt like:
you@mac ~ % _
Windows
Open the terminal:
  Click the Start button (or press the Win key)
  Type "terminal" and press Enter

This opens Windows Terminal (the UI).
The shell inside is PowerShell (the language).
Think of it like: Terminal is to PowerShell
as Terminal.app is to zsh.

You'll see a prompt like:
PS C:\Users\you> _
Linux
Keyboard shortcut (most distros):
  Ctrl + Alt + T

Or find "Terminal" in your app launcher.
The shell inside is bash (Linux default).

user@machine:~$ _
Recommendation
We recommend Hyper on all platforms:
  hyper.is

It's a modern terminal that works on
macOS, Windows, and Linux.

On Windows it runs PowerShell by default.
On macOS/Linux it runs your default shell.

Terminal vs Shell — what's the difference?

The terminal is the window — it handles fonts, colors, tabs, and keyboard input. Examples: Windows Terminal, Terminal.app, Hyper, iTerm2.

The shell is the language running inside that window — it interprets your commands. Examples: bash, zsh, PowerShell.

You can swap either one independently. Run bash inside Windows Terminal, or PowerShell inside Hyper. The terminal is the car; the shell is the engine.

The prompt is just the shell telling you it's ready. The $ or % (or > on PowerShell) means "type here." You don't type the prompt itself — only what comes after it.

Files & Directories

Every OS organizes files in a tree of directories (folders). On macOS and Linux, the top of the tree is called the root, written as /. On Windows, each drive is its own root: C:\.

macOS / Linux
/                    ← root (top of everything)
├── home/
│   └── you/         ← your home directory (~)
│       ├── Documents/
│       ├── Downloads/
│       └── projects/
├── usr/
│   ├── bin/         ← system commands live here
│   └── lib/
├── etc/             ← configuration files
└── tmp/             ← temporary files
Windows
C:\                  ← root of the C: drive
├── Users\
│   └── you\         ← your home directory (~)
│       ├── Documents\
│       ├── Downloads\
│       └── projects\
├── Program Files\   ← installed programs
├── Windows\
│   └── System32\    ← system commands
└── Temp\            ← temporary files
Unix uses forward slashes: /home/you/Documents
Windows uses backslashes: C:\Users\you\Documents
PowerShell actually accepts both, but backslashes are conventional on Windows.

Pathnames

A path is the address of a file. There are two kinds:

TypeStarts withExample
Absolute//home/you/Documents/notes.txt
Relativeanything elseDocuments/notes.txt or ./notes.txt (from your current dir)

Special path shortcuts

SymbolMeaningExample
~Your home directorycd ~ or just cd
.Current directory./run_this.sh
..Parent directory (one level up)cd ..

Navigating

These commands work the same on macOS, Linux, and Windows PowerShell.

macOS / Linux
$ pwd                        # where am I?
/home/you

$ ls                         # what's here?
Documents  Downloads  projects

$ cd Documents               # go into Documents
$ pwd
/home/you/Documents

$ cd ..                      # go back up
$ pwd
/home/you

$ cd                         # go home (no argument = home)
Windows PowerShell
PS> pwd                       # where am I?
C:\Users\you

PS> ls                        # what's here?
Documents  Downloads  projects

PS> cd Documents              # go into Documents
PS> pwd
C:\Users\you\Documents

PS> cd ..                     # go back up
PS> pwd
C:\Users\you

PS> cd ~                      # go home
Good news: pwd, ls, cd, and cd .. all work in PowerShell too. Microsoft added aliases so the basics feel familiar no matter which shell you're in.

Creating and removing

bash / zsh
$ mkdir projects/myapp       # create a directory
$ mkdir -p a/b/c             # create nested dirs

$ touch notes.txt            # create empty file

$ rmdir empty_folder         # remove empty dir
$ rm notes.txt               # remove a file
$ rm -r projects/myapp      # remove dir + contents
PowerShell
PS> mkdir projects/myapp
PS> New-Item a/b/c -ItemType Directory -Force

PS> New-Item notes.txt -ItemType File

PS> Remove-Item empty_folder
PS> Remove-Item notes.txt      # or: rm, del
PS> Remove-Item projects/myapp -Recurse
Deleting is permanent. There is no trash can, no undo — in either shell. Double-check before you press Enter.

Copying, moving, renaming

bash / zsh
$ cp file.txt backup.txt        # copy a file
$ cp -r mydir/ mydir_backup/   # copy a directory

$ mv old.txt new.txt            # rename
$ mv file.txt Documents/        # move
PowerShell
PS> Copy-Item file.txt backup.txt
PS> Copy-Item mydir/ mydir_backup/ -Recurse

PS> Move-Item old.txt new.txt     # or: mv
PS> Move-Item file.txt Documents/
Both shells overwrite silently. If the destination file already exists, it's gone. In bash use cp -i / mv -i for a prompt. In PowerShell use -Confirm.

Listing files in detail

bash / zsh
$ ls -l
drwxr-xr-x  3 you you 4096 Mar 5 10:22 Documents
-rw-r--r--  1 you you  847 Mar 5 09:11 notes.txt

$ ls -la         # include hidden files
$ ls -lh         # human-readable sizes
PowerShell
PS> Get-ChildItem
Mode         LastWriteTime     Length Name
d----  3/5/2026  10:22 AM            Documents
-a---  3/5/2026   9:11 AM       847  notes.txt

PS> Get-ChildItem -Force     # include hidden
PS> Get-ChildItem | Format-Table Name,Length -Auto

Reading the output

bash ls -l: first character is d (directory), - (file), or l (link), followed by permissions, owner, group, size, date, name.

PowerShell Get-ChildItem: Mode column shows d (directory), a (archive), r (read-only), h (hidden), s (system).

Essential Commands

These are the commands you'll use every day. Each one does one thing well. Every section shows bash on the left and the PowerShell equivalent on the right.

Reading files

bash / zsh
$ cat file.txt               # print entire file
$ head file.txt              # first 10 lines
$ head -n 3 file.txt        # first 3 lines
$ tail file.txt              # last 10 lines
$ tail -f log.txt           # follow a file (watch it grow)
$ less file.txt              # scroll through (q to quit)
PowerShell
PS> Get-Content file.txt      # or: cat file.txt
PS> Get-Content file.txt -Head 10
PS> Get-Content file.txt -Head 3
PS> Get-Content file.txt -Tail 10
PS> Get-Content log.txt -Wait  # follow
PS> Get-Content file.txt | Out-Host -Paging

Searching

bash / zsh
$ grep "hello" file.txt
$ grep -i "hello" file.txt   # case-insensitive
$ grep -r "TODO" src/       # recursive
$ grep -n "error" log.txt   # line numbers

$ find . -name "*.txt"      # find by name
$ find . -type d            # dirs only
$ find /tmp -size +100M    # large files
PowerShell
PS> Select-String "hello" file.txt
PS> Select-String "hello" file.txt -CaseSensitive:$false
PS> Get-ChildItem src/ -Recurse | Select-String "TODO"
PS> Select-String "error" log.txt # shows line numbers by default

PS> Get-ChildItem -Recurse -Filter "*.txt"
PS> Get-ChildItem -Recurse -Directory
PS> Get-ChildItem /tmp -Recurse | Where-Object {$_.Length -gt 100MB}

Counting and sorting

bash / zsh
$ wc file.txt                # lines, words, chars
  12   84  527 file.txt

$ wc -l file.txt             # line count only

$ sort names.txt             # alphabetical
$ sort -n numbers.txt       # numeric
$ sort -r names.txt         # reverse

$ uniq sorted.txt            # deduplicate
$ sort names.txt | uniq      # sort then deduplicate
PowerShell
PS> Get-Content file.txt | Measure-Object -Line -Word -Character
Lines: 12  Words: 84  Characters: 527

PS> (Get-Content file.txt).Count   # line count

PS> Get-Content names.txt | Sort-Object
PS> Get-Content nums.txt | Sort-Object {[int]$_}
PS> Get-Content names.txt | Sort-Object -Descending

PS> Get-Content sorted.txt | Get-Unique
PS> Get-Content names.txt | Sort-Object | Get-Unique

Text manipulation

bash / zsh
$ cut -d',' -f1 data.csv    # first CSV column

$ tr 'a-z' 'A-Z' < file.txt # uppercase
$ tr -d ' ' < file.txt      # delete spaces

$ sed 's/old/new/g' file.txt       # replace (print)
$ sed -i 's/old/new/g' file.txt   # replace (in place)
PowerShell
PS> Import-Csv data.csv | Select-Object -First 1
# or for raw split:
PS> (Get-Content data.csv) -split ',' | Select-Object -Index 0

PS> (Get-Content file.txt).ToUpper()
PS> (Get-Content file.txt) -replace ' ',''

PS> (Get-Content file.txt) -replace 'old','new'
PS> (Get-Content file.txt) -replace 'old','new' | Set-Content file.txt

System info

bash / zsh
$ date                  # date and time
$ whoami                # your username
$ hostname              # machine name
$ uname -a             # OS info
$ df -h                # disk space
$ du -sh *             # size of each item
$ ps aux                # running processes
$ top                   # live monitor (q quits)
PowerShell
PS> Get-Date
PS> $env:USERNAME         # or: whoami
PS> $env:COMPUTERNAME     # or: hostname
PS> Get-ComputerInfo | Select-Object OsName,OsVersion
PS> Get-PSDrive -PSProvider FileSystem
PS> Get-ChildItem | ForEach-Object { "{0,10} {1}" -f ($_.Length/1MB).ToString("0.0MB"), $_.Name }
PS> Get-Process            # or: ps
PS> # no built-in top; use Task Manager or:
PS> while($true){Get-Process | Sort-Object CPU -Desc | Select-Object -First 10; Start-Sleep 2; Clear-Host}

Getting help

bash / zsh
$ man ls                # manual page (q quits)
$ ls --help            # quick help (Linux)
$ which python          # where is this command?
$ type cd               # builtin or program?
PowerShell
PS> Get-Help Get-ChildItem         # help page
PS> Get-Help Get-ChildItem -Examples  # just examples
PS> Get-Command python              # where is it?
PS> Get-Command cd | Select-Object CommandType

Input, Output & Pipes

Every command has three channels, and you can rewire them. This works in both bash and PowerShell.

The three channels

ChannelNameNumberDefault
stdinStandard input0Keyboard
stdoutStandard output1Screen
stderrStandard error2Screen

Output redirection

bash / zsh
$ echo "hello" > file.txt       # write (overwrites!)
$ echo "world" >> file.txt      # append
$ cat file.txt
hello
world

$ ls /nope 2> errors.txt        # errors only
$ command &> all.txt             # both stdout+stderr
PowerShell
PS> "hello" | Out-File file.txt         # write
PS> "world" | Out-File file.txt -Append # append
PS> Get-Content file.txt
hello
world

PS> Get-Item /nope 2> errors.txt        # same syntax!
PS> command *> all.txt                   # all streams
> overwrites. If the file exists, its contents are destroyed. Use >> (bash) or -Append (PowerShell) to add to the end.

Input redirection

bash / zsh
$ wc -l < file.txt            # feed file to stdin
2

$ sort < unsorted.txt > sorted.txt
PowerShell
# PowerShell doesn't use < for input.
# Instead, pipe file content in:
PS> (Get-Content file.txt).Count  # line count
2

PS> Get-Content unsorted.txt | Sort-Object | Out-File sorted.txt

Pipes

The | character connects the output of one command to the input of the next. This works in both shells — it's how small tools compose into powerful workflows.

bash / zsh
# How many users are logged in?
$ who | wc -l
5

# How many files here?
$ ls | wc -l
12

# 5 largest files
$ du -sh * | sort -rh | head -5

# Word frequency in a file
$ tr ' ' '\n' < book.txt | sort | uniq -c | sort -rn | head -10

# Error timestamps from logs
$ grep "ERROR" app.log | cut -d' ' -f1,2 | sort | uniq -c
PowerShell
# How many processes running?
PS> Get-Process | Measure-Object
Count: 142

# How many files here?
PS> (Get-ChildItem).Count
12

# 5 largest files
PS> Get-ChildItem | Sort-Object Length -Desc | Select-Object -First 5 Name,Length

# Word frequency in a file
PS> (Get-Content book.txt) -split '\s+' | Group-Object | Sort-Object Count -Desc | Select-Object -First 10

# Error timestamps from logs
PS> Select-String "ERROR" app.log | ForEach-Object { $_.Line.Split(' ')[0..1] -join ' ' } | Group-Object | Sort-Object Count -Desc

The pipeline philosophy

Both shells share the same idea: small commands chained together. Bash pipes text — each command parses and emits strings. PowerShell pipes objects — each command passes structured data with typed properties. The concept is the same; the data format differs.

Filters

A filter is any command that reads input and writes output. These work in pipes:

Taskbash filterPowerShell equivalent
SortsortSort-Object
DeduplicateuniqGet-Unique or Group-Object
Search textgrepSelect-String or Where-Object
First / last Nhead / tailSelect-Object -First / -Last
CountwcMeasure-Object
Extract columnscutSelect-Object (by property)
Transform charstr-replace operator
Find/replacesed-replace or ForEach-Object
Pattern processingawkForEach-Object with -split

How They Differ

By now you've seen the side-by-side commands in every section. This page covers the conceptual differences and the topics not covered elsewhere: variables, environment, and process management.

PowerShell aliases: Microsoft added familiar names like ls, cat, cp, mv, rm as aliases for PowerShell cmdlets. They mostly work, but the flags are different. When in doubt, use the native cmdlet name.

The big difference: text vs objects

Unix shells pass text. PowerShell passes objects.

Unix: Every command outputs a stream of text. You slice it with cut, filter it with grep, and sort it with sort. If the output format changes (an extra column, a different date format), your pipeline breaks.

PowerShell: Commands output .NET objects with typed properties. Get-Process returns Process objects with .CPU, .Id, .Name. You filter with Where-Object and pick fields with Select-Object. The output format can change without breaking your pipeline.

bash — text wrangling
# Get PIDs of node processes
$ ps aux | grep node | awk '{print $2}'
1234
5678
# Fragile: depends on column position
PowerShell — object pipeline
# Get PIDs of node processes
PS> Get-Process node | Select-Object Id
1234
5678
# Robust: .Id is a typed property

Naming conventions

Cryptic vs Discoverable

Unix: short, lowercase, cryptic — ls, grep, awk, sed, wc, chmod. You memorize them.

PowerShell: Verb-Noun, explicit — Get-ChildItem, Select-String, Measure-Object, Set-ItemProperty. You discover them:

PS> Get-Command *process*         # find all process-related commands
PS> Get-Command -Verb Set          # all commands that set something
PS> Get-Command -Noun Item         # all commands about items (files, etc.)

Variables & environment

bash / zsh
$ NAME="world"               # no spaces around =
$ echo "hello $NAME"

$ echo $HOME                 # env variable
$ echo $PATH
$ export API_KEY="secret"    # set for child processes
$ env                        # list all env vars

# Command substitution
$ TODAY=$(date +%Y-%m-%d)
$ echo "Today is $TODAY"
PowerShell
PS> $NAME = "world"            # spaces OK
PS> "hello $NAME"

PS> $HOME                      # built-in variable
PS> $env:PATH                  # env: prefix
PS> $env:API_KEY = "secret"
PS> Get-ChildItem env:         # list all env vars

# Command substitution
PS> $TODAY = Get-Date -Format "yyyy-MM-dd"
PS> "Today is $TODAY"

Process management

bash / zsh
$ ps aux                     # all processes
$ ps aux | grep node         # find by name
$ kill 1234                  # graceful stop
$ kill -9 1234              # force kill
$ command &                 # run in background
$ jobs                       # list bg jobs
$ fg                         # bring to foreground
PowerShell
PS> Get-Process
PS> Get-Process | Where-Object {$_.Name -like "node*"}
PS> Stop-Process -Id 1234
PS> Stop-Process -Id 1234 -Force
PS> Start-Job { command }     # background
PS> Get-Job                   # list bg jobs
PS> Receive-Job -Id 1        # get output

Keyboard & Shortcuts

You don't need a mouse in the terminal. These shortcuts will save you a huge amount of time once they become muscle memory.

Tab completion

The most important shortcut you'll learn

Start typing a filename or command and press Tab. The shell will complete it for you. If there are multiple matches, press Tab twice to see all options.

bash / zsh
$ cd Doc[Tab]
$ cd Documents/           # completed!

$ git sta[Tab]
$ git status              # completed!

$ ls *.tx[Tab]
file.txt  notes.txt  readme.txt  # multiple matches shown
PowerShell
PS> cd Doc[Tab]
PS> cd .\Documents\        # completed!

PS> Get-Chi[Tab]
PS> Get-ChildItem          # completed!

# Keep pressing Tab to cycle through matches
PS> Get-Com[Tab][Tab][Tab]

Command history

Every command you type is saved. Use the arrow keys to scroll through your history instead of retyping.

bash / zsh
Up           # previous command
Down         # next command
Ctrl+R       # search history (type to filter)
$ history    # show full history list
$ !grep      # re-run last command starting with "grep"
$ !!         # re-run the very last command
PowerShell
Up           # previous command
Down         # next command
Ctrl+R       # search history (same!)
PS> Get-History          # show full history
PS> Invoke-History 5     # re-run command #5
F8           # search history by prefix
Ctrl+R is a game changer. Press it, then start typing any part of a previous command. It fuzzy-matches your history in real time. Press Ctrl+R again to cycle through older matches. Press Enter to run it, or Esc to cancel.

Cursor movement

Move around the current line without using the mouse.

KeysActionWorks in
Ctrl+AJump to beginning of linebash, zsh, PowerShell
Ctrl+EJump to end of linebash, zsh, PowerShell
Left / RightMove cursor one characterall
Alt+B / Alt+FMove cursor one word back / forwardbash, zsh
Ctrl+Left / RightMove cursor one wordPowerShell, zsh
HomeBeginning of lineall
EndEnd of lineall

Editing the line

Delete and cut text without reaching for the mouse.

KeysActionWorks in
Ctrl+KDelete from cursor to end of linebash, zsh, PowerShell
Ctrl+UDelete from cursor to beginning of linebash, zsh, PowerShell
Ctrl+WDelete the previous wordbash, zsh
Alt+DDelete the next wordbash, zsh
Ctrl+YPaste back what Ctrl+K/U/W deletedbash, zsh
BackspaceDelete character before cursorall
DeleteDelete character after cursorall
Ctrl+K + Ctrl+Y is "cut and paste" for the command line. Jump to a position with Ctrl+A, kill the rest with Ctrl+K, move somewhere else, paste it back with Ctrl+Y. Faster than selecting with the mouse.

Control commands

KeysActionWorks in
Ctrl+CCancel the current commandall
Ctrl+DExit the shell (or signal end-of-input)bash, zsh
Ctrl+LClear the screenall
Ctrl+ZSuspend current process (resume with fg)bash, zsh
Ctrl+SPause output (freezes terminal)bash, zsh
Ctrl+QResume output (unfreeze)bash, zsh
If your terminal freezes and nothing you type shows up, you probably hit Ctrl+S by accident. Press Ctrl+Q to unfreeze it. This trips up everyone at least once.

Copy & paste in the terminal

macOS Terminal
Cmd+C     # copy selected text
Cmd+V     # paste

# In iTerm2 / Hyper:
# Select text to auto-copy
# Middle-click or Cmd+V to paste
Windows Terminal
Ctrl+Shift+C   # copy (not Ctrl+C!)
Ctrl+Shift+V   # paste

# Or: select text, right-click to copy
# Right-click again to paste

# Ctrl+C in a terminal means "cancel"
# so copy needs the Shift modifier

First Scripts

A shell script is just a text file containing commands. Instead of typing them one at a time, you save them and run the file.

Hello World

greet.sh (bash)
#!/bin/bash
# first argument passed to the script
NAME=$1

if [ -z "$NAME" ]; then
    echo "Usage: ./greet.sh <name>"
    exit 1
fi

echo "Hello, $NAME!"
echo "Today is $(date +%A)."
echo "You are in $(pwd)."
greet.ps1 (PowerShell)
# first argument passed to the script
param([string]$Name)

if (-not $Name) {
    Write-Host "Usage: .\greet.ps1 <name>"
    exit 1
}

Write-Host "Hello, $Name!"
Write-Host "Today is $((Get-Date).DayOfWeek)."
Write-Host "You are in $(Get-Location)."

Running it

bash / zsh
$ chmod +x greet.sh      # make executable (once)
$ ./greet.sh Alice
Hello, Alice!
Today is Thursday.
You are in /home/you.
PowerShell
# no chmod needed on Windows
PS> .\greet.ps1 -Name Alice
Hello, Alice!
Today is Thursday.
You are in C:\Users\you.

The shebang line

Bash scripts start with #!/bin/bash — this tells the OS which program should interpret the file. PowerShell scripts use the .ps1 extension instead; Windows knows to run them with PowerShell.

Variables

bash / zsh
GREETING="Hello"
NAME="World"
echo "$GREETING, $NAME!"

# Command substitution
TODAY=$(date +%Y-%m-%d)
FILES=$(ls | wc -l)
echo "Date: $TODAY, Files: $FILES"

# WRONG — no spaces around =
# NAME = "bad"  <-- error!
PowerShell
$greeting = "Hello"
$name = "World"
"$greeting, $name!"

# Command substitution
$today = Get-Date -Format "yyyy-MM-dd"
$files = (Get-ChildItem).Count
"Date: $today, Files: $files"

# Spaces around = are fine
# $name = "good"  <-- works!

Conditionals

bash / zsh
FILE=$1

if [ -f "$FILE" ]; then
    echo "$FILE is a file."
    echo "$(wc -l < "$FILE") lines"
elif [ -d "$FILE" ]; then
    echo "$FILE is a directory."
else
    echo "$FILE does not exist."
fi
PowerShell
$file = $args[0]

if (Test-Path $file -PathType Leaf) {
    "$file is a file."
    "$((Get-Content $file).Count) lines"
} elseif (Test-Path $file -PathType Container) {
    "$file is a directory."
} else {
    "$file does not exist."
}

Test operators

WhatbashPowerShell
File exists[ -e file ]Test-Path file
Is a file[ -f file ]Test-Path file -PathType Leaf
Is a directory[ -d file ]Test-Path file -PathType Container
String empty[ -z "$var" ]-not $var or [string]::IsNullOrEmpty($var)
Strings equal[ "$a" = "$b" ]$a -eq $b
Numbers equal[ $a -eq $b ]$a -eq $b
Less than[ $a -lt $b ]$a -lt $b
Greater than[ $a -gt $b ]$a -gt $b

Loops

bash / zsh
# Loop over a list
for fruit in apple banana cherry; do
    echo "I like $fruit"
done

# Loop over files
for f in *.txt; do
    echo "$f: $(wc -l < "$f") lines"
done

# Loop with numbers
for i in $(seq 1 5); do
    echo "Count: $i"
done

# While loop
COUNT=0
while [ $COUNT -lt 5 ]; do
    echo "Tick: $COUNT"
    COUNT=$((COUNT + 1))
done
PowerShell
# Loop over a list
foreach ($fruit in "apple","banana","cherry") {
    "I like $fruit"
}

# Loop over files
foreach ($f in Get-ChildItem *.txt) {
    "$($f.Name): $((Get-Content $f).Count) lines"
}

# Loop with numbers
1..5 | ForEach-Object {
    "Count: $_"
}

# While loop
$count = 0
while ($count -lt 5) {
    "Tick: $count"
    $count++
}

Practical example: backup script

backup.sh
#!/bin/bash

SOURCE=$1
if [ -z "$SOURCE" ] || [ ! -d "$SOURCE" ]; then
    echo "Usage: ./backup.sh <dir>"
    exit 1
fi

TS=$(date +%Y%m%d_%H%M%S)
BACKUP="${SOURCE}_backup_${TS}.tar.gz"

tar czf "$BACKUP" "$SOURCE"
SIZE=$(du -h "$BACKUP" | cut -f1)
echo "Backed up to $BACKUP ($SIZE)"
backup.ps1
param([Parameter(Mandatory)]
      [string]$Source)

if (-not (Test-Path $Source -PathType Container)) {
    Write-Error "Not found: $Source"
    exit 1
}

$ts = Get-Date -Format "yyyyMMdd_HHmmss"
$backup = "${Source}_backup_${ts}.zip"

Compress-Archive -Path $Source -DestinationPath $backup
$mb = [math]::Round((Get-Item $backup).Length/1MB, 1)
"Backed up to $backup ($mb MB)"

Quick Reference

Navigation

Taskbash / zshPowerShell
Where am I?pwdGet-Location
Go to directorycd pathSet-Location path
Go homecd or cd ~cd ~
Go up one levelcd ..cd ..
List fileslsGet-ChildItem
List all (hidden too)ls -laGet-ChildItem -Force

Files

Taskbash / zshPowerShell
Read filecat fileGet-Content file
Create filetouch fileNew-Item file
Copycp src dstCopy-Item src dst
Move / renamemv src dstMove-Item src dst
Delete filerm fileRemove-Item file
Delete directoryrm -r dirRemove-Item dir -Recurse
Create directorymkdir dirNew-Item dir -ItemType Directory

Search & filter

Taskbash / zshPowerShell
Search file contentgrep pattern fileSelect-String pattern file
Find files by namefind . -name "*.txt"Get-ChildItem -Recurse -Filter *.txt
Sort linessort fileGet-Content file | Sort-Object
Count lineswc -l file(Get-Content file).Count
Unique linessort | uniqSort-Object | Get-Unique
First N lineshead -n 5 fileGet-Content file -Head 5
Last N linestail -n 5 fileGet-Content file -Tail 5

I/O & pipes

Taskbash / zshPowerShell
Write to filecmd > filecmd | Out-File file
Append to filecmd >> filecmd | Out-File file -Append
Pipecmd1 | cmd2cmd1 | cmd2
Discard outputcmd > /dev/nullcmd | Out-Null
Redirect errorscmd 2> filecmd 2> file

System

Taskbash / zshPowerShell
Current userwhoami$env:USERNAME
Date/timedateGet-Date
Running processesps auxGet-Process
Kill processkill PIDStop-Process -Id PID
Disk spacedf -hGet-PSDrive
Get helpman cmdGet-Help cmd
Clear screenclearClear-Host or cls
Command historyhistoryGet-History

Keyboard shortcuts (work in both)

KeysAction
Ctrl+CStop the current command
Ctrl+LClear the screen
TabAuto-complete file/command names
Up / DownPrevious / next command in history
Ctrl+RSearch command history (bash)
Ctrl+AJump to start of line (bash)
Ctrl+EJump to end of line (bash)

Wildcards (globbing)

PatternMatchesExample
*Any number of characters*.txt → all .txt files
?Exactly one characterfile?.txt → file1.txt, fileA.txt
[abc]One of a, b, or c[Mm]akefile
[0-9]One digitlog[0-9].txt