Library
00/08 · ~38 min
GUIDEDECK · for getting fast at the command line

Linux & the Shell
— small tools,
composed well.

A 38-minute working session on the mental model that makes Unix click: everything is a file, every tool does one job, and a pipe glues them into anything. From navigation and permissions to processes, pipes, and the text toolkit that makes you fast.

~38 MINMIXED TEAMBASH / ZSH
SCROLL
01 · Why the shell 4 min

One idea unlocks Unix:
everything is a file.

A graphical app lets you do one task at a time by clicking. The shell gives you a language for tasks instead — so you can save them, re-run them, and chain them together. And it all rests on one simple idea: files, folders, devices, and even running programs are all handled the same tiny way.

The shell is a read-run-print loop: you type one line, it runs it, shows you the result, and waits for the next line. Two words people mix up: the terminal is just the window on your screen; the shell (programs like bash or zsh) is the little interpreter running inside that window — it reads your text and launches the real program for you.

Terminal vs. shell vs. program

  • The terminal draws the window and shows the text — nothing more.
  • The shell reads the line you typed, figures out which program you meant, and runs it.
  • To find the program, the shell looks through the folders listed in $PATH — a plain list of directories to search. Type git and it finds /usr/bin/git for you.
terminal the window shell bash · zsh · fish reads your line program ls · grep · git found via $PATH you type runs

The terminal is the window; the shell inside it reads your line and launches the program.

"Everything is a file"

  • A regular file, a directory, your disk, your keyboard, a network connection — the kernel (the core of the OS) lets you reach them all with the same four basic operations: open, read, write, close.
  • That's why cat file.txt and cat /dev/null work the same way — both are just bytes you read.
  • It's also why pipeswork at all: one program's output is just bytes another can read.
kernel file API open · read write · close regular file · data.txt directory · src/ device · /dev/null pipe · ls | wc socket · :8080

One small API; every resource looks like a stream of bytes.

GUI — manual & unrepeatable
# resize 200 photos in an image editor: # open each → Image → Resize → 800px → Save… # 200× clicking · no record · can't hand off # did you do #173? who knows.
Shell — a repeatable sentence
for f in *.jpg; do convert "$f" -resize 800 "out/$f" done # one line · repeatable · scriptable · reviewable

Which shell? bash vs. zsh vs. fish

They all run the same commands you'll learn here. They mostly differ in how pleasant they are to type into day to day — how much they help you with completion, history, and suggestions.

bash · the default

The one that's everywhere

The standard shell on almost every Linux server, and what nearly every tutorial and script assumes.

Pro — installed by default everywhere, so your scripts run anywhere without surprises.

Con — plain out of the box: no live suggestions, and its tab-completion feels dated next to the others.

zsh · the upgrade

bash, but nicer to live in

Runs your bash commands almost unchanged, but adds smarter completion and themes (via add-ons like Oh My Zsh). The default on macOS.

Pro — a big comfort boost while staying bash-compatible, so little has to be relearned.

Con — the best parts need plugins and a config file before they shine.

fish · the friendly one

Helpful with zero setup

Designed to be pleasant from the first run: it suggests commands as you type and colors your line to catch typos — no config needed.

Pro — the friendliest experience out of the box; great for newcomers.

Con— its syntax isn't bash-compatible, so bash scripts and copy-pasted snippets won't run as-is.

How to choose  Write your scripts in bash so they run on any machine. For your own daily typing, pick zsh or fish for the nicer experience — the commands you learn carry over to all three.

02 · Distributions 4 min

One kernel,
many distributions.

"Linux" is really just the kernel. What you install is a distribution — that kernel wrapped with a package manager, default tools, and a release policy. Pick the wrapper that fits your team; the shell underneath is identical everywhere.

A distribution ("distro") the Linux kernel + a package manager + a curated set of tools and a release policy, bundled and supported as one OS. The kernel is shared; distros differ in packaging, defaults, cadence and support — not in the commands you type day to day.
apt · .deb

Debian / Ubuntu

The friendly default. Huge community, vast package set, rock solid. Ubuntu adds commercial backing (Canonical) and predictable LTS releases — the safe pick for most servers and desktops.

dnf · .rpm

RHEL / Fedora / Rocky

The enterprise lineage. RHEL ships paid long-term support; Rocky & Alma are free, bug-for-bug rebuilds; Fedora is the fast-moving upstream. Choose it where support and certified stability matter.

pacman · rolling

Arch

Minimal and DIY — you assemble the system and it updates continuously (rolling release). Bleeding-edge and superbly documented (the Arch Wiki). Great for control and for learning how Linux fits together.

apk · musl

Alpine

Tiny and security-focused (~5 MB). Built on musl libc and busybox — the de-facto base image for containers, where every megabyte and CVE counts.

Same intent, different manager

# install nginx — one idea, four managers apt install nginx # Debian / Ubuntu (.deb) dnf install nginx # RHEL / Fedora / Rocky (.rpm) pacman -S nginx # Arch apk add nginx # Alpine (containers)

Like different grocery chains: the food is the same, only the aisles and checkout differ.

How to choose

  • Server / team defaultUbuntu LTS or Debian: ubiquitous, stable, easy to hire for.
  • Enterprise & support contractsRHEL (or free Rocky / Alma) for long, certified lifecycles.
  • ContainersAlpine or a distroless base — small surface, fast pulls.
  • Learning / full controlArch: you build it up and understand every piece.
  • The rule: the distro decides packaging, cadence and support — not your shell skills. Those transfer everywhere.
03 · Filesystem & navigation 5 min

One tree, rooted at /.
Learn to move through it.

There are no drive letters. Every disk, USB stick, and network mount hangs off a single tree that starts at /. Master a handful of moves and you stop thinking about where things are.

The filesystem is one hierarchy rooted at / (root). A path is an address in that tree. Absolute paths start at / (/etc/hosts); relative paths start from where you stand — your working directory (pwd).
/ /bin /etc /home /var /usr /tmp maria/ alex/ ~ ≡ /home/maria

Everything descends from /. ~ is a shortcut to your own /home/<you>.

The five shortcuts that matter

  • . — here (the current directory).
  • .. — one level up.
  • ~ — your home directory.
  • - — the directory you were just in.
  • Tab — autocomplete names; the single most underused key.

pwd says where you are, lsshows what's here, cd moves you.

Retyping the whole address
cd /home/maria/projects/api/src/handlers # …go look at something else, then: cd /home/maria/projects/api/src/handlers # type the same 40 characters again
Relative moves + shortcuts
cd ~/projects/api # ~ = your home cd src/handlers # relative to here cd .. # back up one level cd - # jump to the previous dir
ls — see what's here

Read the listing

Plain ls hides detail. The flags you'll live in: -l long form, -a include hidden (dot-files), -h human sizes, -t newest first.

ls -lah # -rw-r--r-- 1 maria web 2.4K config.yml # perms links owner group size name
globbing — match by pattern

Let the shell expand names

The shell expands *, ?, and […] into matching filenames before the command runs — so any command gets pattern matching for free.

ls *.log # every .log here ls **/*.test.ts # recursive (globstar) ls report-?.csv # one char: report-1.csv… rm draft-[0-9].md # a character class
04 · Permissions & ownership 5 min

Who can read, write,
and execute — and the answer per file.

Every file carries an owner, a group, and three permission sets — for the owner, the group, and everyone else. Once you can read rwxr-xr-x at a glance, permissions stop being scary.

Permissions answer who may do what to a file. Three actions — read, write, execute — are granted to three audiences: the user (owner), the group, and others. That's the nine letters in rwxr-xr-x.
- rwx r-x r-- TYPE USER GROUP OTHER 7 5 4 4+2+1 4+0+1 4+0+0

-rwxr-xr-x = 755. Octal is just r=4, w=2, x=1 added up per audience.

chmod — change mode

Octal or symbolic

Set bits numerically (755) or adjust them by letter (+x, g-w). On directories, x means "may enter", not "may run".

chmod 644 config.yml # rw- r-- r-- chmod +x deploy.sh # make runnable chmod g-w report.txt # group: drop write
chown — change owner

Owner and group

chown user:group hands a file to a new owner and/or group. -R recurses a directory tree. Usually needs sudo.

chown maria config.yml chown maria:web config.yml chown -R maria:web /srv/app
root & sudo

The superuser

root bypasses every permission check. sudo runs one command as root — borrow the power, give it back. Prefer it to logging in as root.

sudo systemctl restart nginx # run a single command as root # then you're back to being you
chmod 777 — the classic hole
chmod 777 deploy.sh # rwx for EVERYONE — anyone can rewrite # the script your server runs as root chmod -R 777 /var/www # …now any process can tamper with the site
Least privilege — grant the minimum
chmod 755 deploy.sh # you: rwx · others: r-x chmod 644 config.yml # you: rw- · others: r-- chmod 600 .env # secrets: only you chown maria:web config.yml

Like an office: the owner has a key (rwx), their team can enter the room (r-x), the public can only read the sign on the door (r--). 777 props every door open.

05 · Processes, jobs & signals 5 min

Every command is a process.
You can watch, pause, and talk to it.

A running program is a process with a numeric PID. You steer processes by sending signals — small, named messages — and you juggle several at once with jobs.

A process is a running instance of a program — it has its own slice of memory, its own open files, and an ID number called a PID. A signal is a one-word message the kernel delivers to it: SIGTERM (please stop), SIGKILL (stop now, no questions), SIGINT (Ctrl-C), SIGHUP (the terminal closed).
PID 4821 running SIGINT · Ctrl-C SIGTERM · kill SIGKILL · kill -9 flush + cleanup → graceful exit stopped instantly no cleanup

SIGTERM asks nicely and lets the process clean up. SIGKILLcan't be caught — last resort only.

Look before you leap

  • ps aux — a snapshot of every process (owner, PID, CPU, memory, command).
  • top / htop — the live view; sort by CPU or memory.
  • pgrep nginx — find PIDs by name; pkill nginx signals them.
  • kill -l — list every signal and its number.
ps aux | grep node # maria 4821 3.1 1.2 node server.js kill 4821 # SIGTERM by default
Reaching for kill -9 first
kill -9 4821 # SIGKILL: the process dies mid-write # → half-flushed files, stale lock files, # orphaned children, corrupt state
Ask first, force only if needed
kill 4821 # SIGTERM (15): "please shut down" # …let it flush buffers + release locks… kill -9 4821 # SIGKILL only if it's truly stuck
jobs — many tasks, one terminal

Foreground & background

& starts a command in the background. Ctrl-Z suspends the foreground one; bg/fg resume it behind or in front.

./backup.sh & # run in the background jobs # [1]+ Running ./backup.sh fg %1 # pull job 1 to the front
survive the hang-up

Keep running after logout

Closing the terminal sends SIGHUP to its children. nohup (or a terminal multiplexer like tmux) detaches a task so it keeps going.

nohup ./long-import.sh & # immune to SIGHUP; logs to nohup.out # for real work, prefer tmux / screen
06 · Pipes & redirection 5 min

Small tools, glued with |.
Text is the universal interface.

Every program has three streams: stdin, stdout, and stderr. Redirection rewires where they go; a pipewires one program's output straight into the next's input. That's the whole Unix superpower.

The three streams — every process is born with stdin (0), stdout (1), and stderr (2). By default stdin is your keyboard and stdout/stderr are your screen. The shell lets you point any of them somewhere else.
grep ERROR app.log sort | uniq -c aggregate stdout(1) → stdin(0) the pipe | terminal stderr(2) still prints here

A pipe joins stdoutstdin. Note that stderr is not piped by default — a common gotcha.

redirection — > >> < 2>

Send a stream to a file

cmd > out.txt # stdout → file (overwrite) cmd >> out.txt # stdout → file (append) cmd 2> err.txt # stderr → file cmd > all 2>&1 # both → same file cmd < input.txt # file → stdin cmd > /dev/null # discard output entirely
the connectors

tee, xargs & friends

tee splits a stream to a file and onward. xargs turns stdin into command arguments — the bridge for tools that read args, not stdin.

make 2>&1 | tee build.log # watch it AND save it find . -name "*.tmp" | xargs rm # feed each result as an argument to rm
Temp files for every step
grep ERROR app.log > tmp1 sort tmp1 > tmp2 uniq -c tmp2 > tmp3 rm tmp1 tmp2 tmp3 # bookkeeping + disk churn
One stream, end to end
grep ERROR app.log | sort | uniq -c # data flows through memory — # no temp files, no cleanup, # each tool does exactly one job
07 · The text toolkit 6 min

Four tools cover
most day-to-day text work.

grep finds lines, sed edits streams, awk works in columns, and findwalks the tree. Learn their core flags and you can answer almost any "where / what / how many" question without writing a script.

These are filters: each reads text on stdin (or files), transforms it, and writes to stdout — so they snap together with pipes. Master one flag set at a time; you rarely need them all at once.

grep — find lines that match

grep -r "TODO" src/ # recurse a directory grep -i "error" app.log # case-insensitive grep -n "port" config.* # show line numbers grep -c "200 " access.log # count matching lines grep -v "DEBUG" app.log # invert: lines WITHOUT it grep -E "4[0-9]{2}" log # extended regex: 4xx codes
Reach for it when
You need to locate something — a string, a pattern, an error — across files or a stream.
Remember
-r recurse, -i ignore case, -n line numbers, -v invert.

sed — edit a stream, line by line

sed 's/foo/bar/' file # replace first per line sed 's/foo/bar/g' file # replace all (global) sed -i 's/v1/v2/g' *.yml # edit files IN PLACE sed -n '10,20p' file # print only lines 10–20 sed '/^#/d' conf # delete comment lines
Reach for it when
You want a find-and-replace across many files, or to extract a line range, non-interactively.
Careful
-i rewrites files — test without it first, or keep a backup (-i.bak).

awk — think in columns

awk '{print $1}' access.log # first field awk -F: '{print $1}' /etc/passwd # : as delimiter awk '$3 > 500' sizes.txt # filter on a column awk '{s += $2} END {print s}' data # sum a column awk '/ERROR/ {print $1, $5}' log # match + select
Reach for it when
Data is whitespace- or delimiter-separated and you want a specific column, a filter, or a quick sum/average.
Mental model
$0 is the whole line; $1…$n are the fields; -F sets the separator.

find — walk the tree by criteria

find . -name "*.log" # by name find . -type f -mtime -1 # files changed < 1 day find . -size +100M # bigger than 100 MB find . -name "*.tmp" -delete # match, then delete find . -name "*.js" -exec wc -l {} \;
Reach for it when
You need files by attribute — name, type, size, age — anywhere in a tree, then act on each.
Pairs with
-exec … or | xargs to run a command per match.

The glue: sort, uniq, cut, wc

cut -d, -f1,3 data.csv # columns 1 & 3 of a CSV sort access.log | uniq -c # count duplicate lines sort -rn -k2 sizes.txt # numeric, descending, by col 2 wc -l *.log # count lines per file cut -d' ' -f1 access.log | sort | uniq -c | sort -rn
The pattern
The last line is the Unix money move: extract → sort → count → rank. Top IPs, busiest endpoints, noisiest errors — all the same pipeline.
Note
uniq only collapses adjacent duplicates, so sort always comes first.

tail & head — watch a log in real time

tail -n 50 app.log # last 50 lines head -n 20 app.log # first 20 lines tail -f app.log # follow: stream new lines live tail -F /var/log/app.log # keep following across rotation tail -f app.log | grep -i error # live, filtered feed
Reach for it when
You want a realtime view of a log — watch requests, errors, or a deploy stream in as they happen, instead of re-running cat.
Remember
-f follows the file; -F survives log rotation; pipe into grep to follow only what matters. Ctrl-C to stop.
08 · Everyday recipes & recap 4 min

Small habits that
keep you fast and safe.

A few bad → good swaps that show up in everyone's history, then five rules to walk out with.

Useless use of cat
cat app.log | grep ERROR # an extra process for nothing
grep reads files itself
grep ERROR app.log # one process, same result
Parsing ls output
for f in $(ls); do# breaks on spaces & newlines
Let the glob do it
for f in *.txt; do# safe with any filename
Unquoted, unguarded rm
rm -rf $DIR/ # if $DIR is empty → rm -rf / (disaster)
Quote & fail fast
set -u # unset var = error rm -rf "${DIR:?}/" # refuse if empty
1Everything is a file.One API for files, directories, devices, and pipes — that's why the tools compose at all.
2Compose small tools. One job each, joined by |. Text is the universal interface between them.
3Least privilege. Grant the minimum (644/755); never chmod 777.
4Signal gently. SIGTERM lets a process clean up; keep SIGKILL (-9) for genuine hangs.
5If you typed it twice, script it. The shell is a programming language — quote your variables and let it work for you.

Keep going

  • The Linux Command Line — William Shotts (free PDF, linuxcommand.org)
  • explainshell.com — paste any command, see every flag explained
  • man & tldr — the manual, and the friendly example-first version
  • ShellCheck — a linter that catches the quoting bugs above

Start every script with set -euo pipefail: exit on errors (-e), treat unset variables as errors (-u), and fail a pipeline if any stage fails (-o pipefail). It turns silent corruption into a loud, early stop.

One sentence to remember

"Write programs that do one thing well. Write programs to work together. Make text the universal interface."

— Doug McIlroy, the Unix philosophy

Knowledge check

Did it stick?

Five quick questions on the shell, permissions, processes, pipes, and the text toolkit — instant feedback, no sign-in.

Rate this deck
be the first

Navigate with ← → or scroll · back to library