Docker Deep Dive · Prerequisites · P1 of 7
The Terminal & the Shell
Docker and Kubernetes are driven from one place: a shell. Every
docker command, every Dockerfile RUN line, every
kubectl exec lands you here — so before the whale, meet the three programs
hiding behind your blinking cursor.
Before you start — any OS is fine
You need Docker Desktop (or Docker Engine on Linux) installed and running — chapter 00-setup. Not because this lesson is about Docker, but because your computer probably isn't Linux — and even if it is, a container is the fastest disposable Linux machine you'll ever own. The main lab runs inside one, so it is identical on macOS, Windows, and Linux. Grab the chapter:
cd docker-deep-dive
git pull
cd p1-terminal-and-shell
Every docker … line in this course is the same on all three OSes. If you
started the course here and haven't done Lesson 1 yet: whenever a
docker run … line appears, just paste it — Lesson 1 explains every flag.
1Three programs pretending to be one
What you casually call "the terminal" is a sandwich of three very different programs:¹
- The terminal emulator (Terminal.app or iTerm on macOS, Windows Terminal, GNOME Terminal or Konsole on Linux…) draws text on screen and forwards your keystrokes. It understands nothing about commands.
- The shell is the interpreter: it reads a line, splits it into words, finds the program you named, asks for it to be run, then reports how it went. Which one you have depends on your OS — zsh on modern macOS, bash on most Linux distros, PowerShell on Windows — and bash (or sh) inside almost every container.²
- The kernel is the one program that actually runs programs and touches hardware — disk, network, memory. Everything above it must ask.
docker exec -it web bash
means: attach my terminal to a bash shell whose kernel is
Linux. Your host's shell and kernel vary by OS; the container side never does —
which is exactly why this course practices inside containers.⁴Prompt anatomy. The shell signals who and where you are before every
command. The convention survives across systems: zsh ends its prompt in %,
bash in $ for a normal user — and # when you are
root, the all-powerful admin user. (PowerShell writes
PS C:\Users\you> — different costume, same job.) Inside containers
you'll see # a lot; P2 explains why that matters.
2The 40 milliseconds after you press Enter
Type ls -l /var/log and hit Enter. Here's the round trip — step through
it:²
- Enter pressed. The terminal understands none of it — it hands the raw text to the shell.
- The shell splits the line into words: a command, a flag, an argument. (Quoting rules — P3 — decide where words end.)
- The shell hunts for a program named "ls" through its PATH list of directories, left to right; first hit wins. P4 owns PATH.
- The shell can't execute anything itself — it asks the kernel to start /usr/bin/ls. (P5 gives this ask its real name: a syscall, execve.)
- Now ls does its one job: it asks the kernel for /var/log's entries. Only the kernel touches the disk.
- Output bytes flow back and the terminal draws them; ls exits with code 0 = success, and the shell prints a fresh prompt.
RUN pip install … in a Dockerfile and every
command you'll ever type into docker exec.²3Command anatomy, and how to ask for help
command -f --long-flag argument1 argument2
# ls -l /var/log → short flags stack: ls -lah = -l -a -h
Help systems, by where you're standing:
ls --help # quick flag list — works in every container
man ls # the full manual — on macOS & Linux hosts (Windows: Get-Help dir)
type ls # what IS this command, really? (section 5 shows why this matters)
But try man ls inside a container and you get a surprise —
images are stripped to stay small, so the manual pages simply aren't in there:
man ls
This system has been minimized by removing packages and content that are
not required on a system that users do not log into.
To restore this content, including manpages, you can run the 'unminimize'
command. You will still need to ensure the 'man-db' package is installed.
Rule of thumb: --help inside containers; man on a
macOS/Linux host; Get-Help if you're in PowerShell.⁵
| key | effect (bash & zsh; most also work in PowerShell) |
|---|---|
Tab | complete the command/path — if it won't complete, it doesn't exist (your best typo detector) |
↑ / ↓ | walk back through command history |
Ctrl-R | search history as you type |
Ctrl-C | interrupt the running command (P4 reveals what this really sends) |
Ctrl-L | clear the screen; history stays |
4Lab A — move around, inside your Linux box
Your Linux playground — the one-liner of this whole phase
docker run -it --name linuxbox ubuntu:24.04 bash
A full Ubuntu, disposable, in about a second — and the same Ubuntu no matter what your host OS is. (New here? Don't worry what the flags mean — Lesson 1 explains them. For now: paste, and you're in Linux.)
Your prompt changes to something like root@bd5ceeacf242:/# — user
root (that trailing #!), hostname = the container's ID.
You are the admin of this tiny machine; P2 explores what that means. First, look at the
land:
ls /
bin dev home media opt root sbin sys usr
boot etc lib mnt proc run srv tmp var
That's the standard Linux tree — /etc for config, /var for
logs and data, /root is root's home. P2 gives you the guided tour; it's
also exactly what a Docker image is. Now learn to move. The filesystem is one
tree growing from the root, /. An absolute path starts
there (/var/log); a relative path starts from where you
are. Three shorthands: . here · .. up one · ~
your home.¹
pwd # where am I? trust this, not the prompt
cd /var/log # absolute: same result from anywhere
pwd
cd ~ # home again (plain cd does the same)
pwd
cd - # bounce back to wherever you just were
/
/var/log
/root
Now build something. You'll make a small notes project, reorganize it, and tear part of it down — the exact moves you'll later do inside images:
cd ~
mkdir -p harbor-notes/docs harbor-notes/drafts # -p makes parents too
cd harbor-notes
touch docs/setup.md docs/commands.md drafts/idea.txt
find . # show the whole tree
.
./docs
./docs/commands.md
./docs/setup.md
./drafts
./drafts/idea.txt
cp -r docs docs-backup # -r = recursive, needed for directories
mv drafts/idea.txt docs/idea.md # mv moves AND renames — same command
rm -r docs-backup # gone. no trash. no undo. read twice, then Enter
ls -l docs
total 0
-rw-r--r-- 1 root root 0 Jul 17 03:12 commands.md
-rw-r--r-- 1 root root 0 Jul 17 03:12 idea.md
-rw-r--r-- 1 root root 0 Jul 17 03:12 setup.md
Peek at details with ls -lah (long, all, human sizes) — for now read
just names, sizes, dates. The cryptic rw-r--r-- column on the left is the
entire subject of P2. To page through any long file:
less filename — space scrolls, /word searches,
q quits. Stay in the box (or hop back in with the same
docker run line after an exit) — Lab B uses it.
5Your own terminal — three dialects, one common ground
Everything in Lab A also works natively on macOS and Linux, because their shells and
tools come from the same POSIX family. But "the same family" is not "identical" — and
Windows speaks a different language entirely. Ask your own shell what ls
really is:
type ls # bash/zsh (PowerShell: Get-Command ls)
# macOS (the instructor's themed zsh):
ls is an alias for eza --icons --grid --group-directories-first
# ubuntu:24.04 container (interactive bash):
ls is aliased to `ls --color=auto'
# Windows PowerShell (documented default):
Alias ls -> Get-ChildItem
On all three systems, ls isn't plain ls.
macOS setups often alias it to a modern tool like
eza; Ubuntu quietly adds color flags;
Windows PowerShell maps it to a completely different program,
Get-ChildItem, whose flags share nothing with Unix
ls.⁶
type (or Get-Command) is the truth-teller: it reveals aliases,
builtins, and real binaries — ask it whenever a command behaves unlike the tutorial
you're reading.
Even the "real" ls (aliases aside) differs by host — same name, three
different programs:³
| host | what answers to "ls" | tell-tale |
|---|---|---|
| macOS | BSD ls | /bin/ls --version → unrecognized option — BSD has no --version |
| Linux | GNU ls (coreutils) | ls --version → ls (GNU coreutils) … |
| Windows PowerShell | Get-ChildItem — not ls at all | Unix flags like ls -lah simply error |
You only own one of those rows — no need to test the others. The check everyone can run, because every learner has the same container:
ls --version
ls (GNU coreutils) 9.4
Copyright (C) 2023 Free Software Foundation, Inc.
This is the point of the whole section: your host is a dialect; the container
is the standard. Whatever terminal you own — zsh on a Mac, bash on Fedora,
PowerShell on Windows — the linuxbox gives every learner the identical
GNU/Linux environment, and that's the dialect Dockerfiles and Kubernetes manifests
speak.
Windows users — two good options
Option 1 (used in this course): do everything inside the
linuxbox container — your PowerShell only ever types
docker … lines, which are identical on every OS. Option 2:
install WSL 2
(Windows Subsystem for Linux) and get a genuine Ubuntu terminal on Windows — Docker
Desktop on Windows already runs on WSL 2 under the hood, so you have half of it
installed.⁷
Don't hand-translate the labs into PowerShell syntax — the flags genuinely don't map.
6Lab B — disposability, and your first graded exercise
exit # leave the shell → the container stops
docker rm linuxbox # throw the whole machine away
docker run -it --name linuxbox ubuntu:24.04 bash
ls /root # … empty. your tree is gone
Fresh box, no harbor-notes. Containers forget — you met this in
Lesson 1, and it's why anything worth keeping lives in an image (Lesson 3) or a volume
(Lesson 5). Here it's a feature: you can never wreck this machine in a way
docker rm won't fix.
Finish with the feedback loop. The chapter ships check.sh, a grader for
the tree you built. Exit the box, then — from the chapter folder — start one that can
see it (-v shares the folder at /labs; lesson 5 explains):
exit
docker rm linuxbox
docker run -it --rm -v "$PWD":/labs ubuntu:24.04 bash
docker run -it --rm -v "${PWD}:/labs" ubuntu:24.04 bash
cd /root
mkdir -p harbor-notes/docs harbor-notes/drafts
cd harbor-notes
touch docs/setup.md docs/commands.md drafts/idea.txt
cp -r docs docs-backup
mv drafts/idea.txt docs/idea.md
rm -r docs-backup
sh /labs/check.sh /root/harbor-notes
PASS harbor-notes/ exists
PASS docs/ exists
PASS drafts/ exists
PASS docs/setup.md exists
PASS docs/commands.md exists
PASS docs/idea.md exists (moved from drafts/ and renamed)
PASS drafts/idea.txt is gone (it moved)
PASS docs-backup/ is gone (rm -r cleaned it up)
----
8 passed, 0 failed
All good — tree matches the lab. ✔
Any FAIL line tells you which move to redo — fix it and run the grader again. (On a
macOS/Linux/WSL host you can also do the whole lab natively and grade it with
sh check.sh ~/harbor-notes — the grader is the same either way.)
You can now
- Name the three layers under your cursor — terminal, shell, kernel — and say which one parses, which one runs, and which one draws.
- Navigate any Linux machine:
pwd,cd, absolute vs relative paths,...~. - Build and reshape file trees with
mkdir -p,touch,cp -r,mv,rm -r— in a container that's identical on every OS. - Ask any command to explain itself (
--help,man,type/Get-Command) and spot when "ls" isn't really ls — on macOS, Ubuntu, and Windows. - Spin up and destroy a disposable Linux machine at will, from any host OS.
7Check yourself
You type ls -l /var/log and press Enter. Which
program splits the line into words and hunts for ls?
- the terminal emulator
- the shell itself
- the Linux kernel
- the ls program
Your current directory is /var/log. Where does
cd ../tmp land you?
- /tmp
- /var/tmp
- /var/log/tmp
- /log/tmp
Inside the ubuntu:24.04 container, man ls prints a
notice instead of the manual. Why?
- the image was minimized
- the network is offline
- man requires root privileges
- bash lacks man support
One line each: what do the terminal, the shell, and the kernel contribute when you run a command?
Terminal: draws text and forwards keystrokes — understands no commands. Shell: parses the line, finds the program via PATH, asks for it to run, reports the exit code. Kernel: actually starts the program and performs all disk, network, and memory work.
On three different machines, ls turned out to be an
alias for eza (a Mac), for ls --color=auto (Ubuntu), and for
Get-ChildItem (Windows PowerShell). What command exposes this, and why
does it matter the moment you step into a container?
type (in bash/zsh; Get-Command in PowerShell) reveals what a name really resolves to — alias, builtin, or binary. It matters because your host's aliases and dialect don't exist inside a container: there you get plain GNU tools, so flags and output can differ from what your own terminal taught you to expect. Ask type before you rely on a command behaving the way a tutorial shows.
8Go deeper
Primary source: The Linux Command Line by William Shotts —
free book at linuxcommand.org. Chapters
1–4 cover this lesson's ground with more leisure: what shells are, navigation, and file
manipulation. It's the book this whole phase leans on. (Windows readers: everything in
it applies inside your linuxbox or WSL.)
Next: that rw-r--r-- column you were told to ignore,
the mysterious root, and the guided tour of /etc and friends —
P2: Files, Permissions & root.
The Linux tree you just glimpsed with ls / is, byte for byte, what a Docker
image contains.
Stuck? Curious?
Bring questions to class, or open an issue on the course repo — include the command you ran and the output you got. The quizzes above are for self-checking: commit to an answer before revealing it, and re-try anything you missed tomorrow.
Sources
- Shotts — The Linux Command Line, ch. 1–4 (shell, navigation, file manipulation)
- GNU Bash Reference Manual — Command Search and Execution (word splitting, PATH lookup)
- GNU Coreutils manual (ls, cp, mv, rm semantics; GNU vs BSD flags)
- Docker Docs — docker exec (interactive shells in containers)
- Ubuntu — minimized images (why man pages are stripped)
- Microsoft Learn — about_Aliases (ls → Get-ChildItem in Windows PowerShell)
- Docker Docs — Docker Desktop WSL 2 backend (Windows runs containers via WSL 2)