The shell gets a prominent spot on the poster because it is the center of how you use Unix: you type commands, launch programs, chain them together with pipes, and tell the system what to do. What set it apart is that the shell is an ordinary program, not part of the kernel, so anyone could write a better one. In their 1974 paper, Ritchie and Thompson put it this way: because the shell runs as “an ordinary, swappable user program”, it “may be made as powerful as desired at little cost.”
People did write better ones. The first Unix shell was the
Thompson shell (sh), written
by Ken Thompson for the earliest versions of Unix in 1971. It handled command
execution, redirection, and pipes, but wasn’t a real programming language. The
Mashey shell and Bill Joy’s
csh came next, both adding scripting
features (see #14). Then in 1979, Stephen
Bourne’s sh shipped with
Version 7 Unix and became the template everything else (ksh, bash, zsh)
built on.
AWK is a tiny programming language aimed squarely at text: read input line by line, match patterns, run actions, emit output. Great on its own, better inside a pipeline.
It was written at Bell Labs in the 1970s. The name comes from the three authors’ initials: Alfred Aho, Peter Weinberger, and Brian Kernighan — the same “k” as in #5.
B is the step between BCPL and C. Ken Thompson wrote it at Bell Labs around 1969 for the early Unix work on the PDP-7, with contributions from Dennis Ritchie. It borrowed heavily from BCPL — the name is widely taken to be a contraction — and dropped most of BCPL’s complexity to fit on a machine with very little memory.
B was typeless: everything was a machine word. That worked on the PDP-7 but fell apart on the PDP-11, which cared about byte-sized data. Ritchie added types, kept most of the syntax, and called the result C. B didn’t survive the transition, and today it exists mostly as a footnote in the lineage BCPL → B → C.
When two processes talk over a pipe, one is producing data and the other
is consuming it. The kernel holds a small buffer in between. If the
producer gets ahead, the buffer fills, and the kernel blocks the
producer’s next write until the consumer drains some space. That’s
backpressure: the slow side telling the fast side to wait.
Whether the valve on the poster’s pipe is a deliberate nod to this or just a nice bit of plumbing, I’ll leave to you.
The poster’s title is set in big block letters, much like the output of the
banner command. banner takes a
string and prints it in oversized letters built from ordinary characters. On a
shared line printer that was practical: each job started with a separator page
carrying the owner’s name in letters big enough to spot in a pile of fanfold
paper.
Two versions survive. System V’s banner prints horizontally at a fixed size.
The BSD one, by Mary Ann Horton at Berkeley, prints sideways down the page, so
the message can run as long as the paper does. FIGlet does the same job today
with many more fonts.
I have to admit, this object looks more like a boot than a sock. But it’s hard to believe the artist would leave out sockets, given how central they are to Unix and operating systems generally. So I see two readings:
See the Wikipedia entry on Berkeley sockets
or the socket(2)
man page for the API itself.
cat takes its name from
(con)catenate. Give it one file and it dumps the contents to stdout;
give it several and it glues them together. It was part of Version 1
Unix, written by Ken Thompson and Dennis Ritchie.
cat is also where the “Useless Use of Cat” joke comes from:
cat file | grep foo # UUOC
grep foo file # same thing, one less process
The curses
library hides the ugly details of moving the cursor around a terminal and
repainting regions of the screen. Before curses, programs that wanted to
do anything more than scroll text had to emit raw escape sequences for
whatever terminal they were running on — and every terminal model spoke
a slightly different dialect. Curses reads the terminal’s capabilities
from termcap/terminfo and gives you a portable API.
It’s why vi, less, top, htop, mutt, and many other TUI
programs look and work the same across terminals. The name is a pun on
“cursor optimization”. Ken Arnold wrote the original curses for BSD Unix.
Daemons are long-running
background processes, usually started at boot, that do the system’s chores:
cron runs scheduled jobs, syslogd collects log messages, sshd accepts
logins. On Unix they are ordinary processes, not part of the kernel, so you can
stop, restart, or replace a service without rebooting the machine.
The name comes from MIT’s Project MAC around 1963. Fernando Corbató’s team
borrowed it from Maxwell’s demon, the imaginary being in physics that quietly
sorts molecules, for background processes that work tirelessly on system
chores. Unix daemons usually advertise it with a trailing d.
date prints or
sets the system time. Under the hood, Unix stores time as a single
count — seconds since 00:00:00 UTC on 1 January 1970, the “Unix epoch”.
The developers picked it because it was recent and round, which made it
easy to reason about.
A signed 32-bit seconds counter overflows at 03:14:07 UTC on
19 January 2038. That’s the Y2038 problem, and it’s still live: many
embedded systems and filesystems store 32-bit time_t. Modern Unixes use a
64-bit time_t. It buys around 292 billion years of headroom, which is
probably enough.
diff compares two files line by line and
shows what was added, removed, or changed. Doug McIlroy and James Hunt wrote it at
Bell Labs, and it shipped with Fifth Edition Unix in 1974. Its output, the patch,
became a universal unit of change.
Before diff, reconciling two versions of a source file meant reading them side by
side. diff automated that, and once Larry Wall’s
patch(1) arrived in 1985,
you could mail someone a diff and they could apply it to their own copy of the
file.
Hunt and McIlroy described the algorithm in a 1976 paper. It finds the longest common subsequence of the two files’ lines, which is expensive to compute naively, and they made it fast enough for the hardware of the time. Most tools today use Eugene Myers’s 1986 algorithm instead; it’s the default in both GNU diff and git.
Three sets of initials, three people who shaped Unix:
ken, which is how he’s cited in most early Unix
source.awk
(see #15).The tree the wizard is working on probably stands for the Unix filesystem
hierarchy. Unix organizes files and directories as a tree rooted at /,
branching into subdirectories like /usr, /bin, and /etc. You navigate it with commands like cd, ls, and pwd.
The branching form could also represent process trees. Every process in Unix
has a parent, forming a tree rooted at init (PID 1).
fork(2) is how Unix
makes a new process: the kernel duplicates the calling process, and now
there are two of them running the same code. They only differ in the
return value of fork itself — zero in the child, the child’s PID in the
parent — which is how each copy knows who it is.
What made this radical was how cheap it could be. Early
Unix leaned on it for everything, and modern kernels use copy-on-write so
the duplicated address space costs almost nothing until one side writes
to it. The shell runs every command by forking and then calling exec
in the child to replace itself with the target program. That fork/exec
split is unusual — VMS and Windows went with a single “spawn” call that
creates a process already running a different program — but it’s what
lets the shell set up redirections, pipes, and environment changes in
the child after fork and before exec, using ordinary code. Most of
Unix’s composability comes from that split.
Melvin Conway described the idea under the name “fork” in A Multiprocessor System Design in the early 1960s, years before Unix existed.
login is the step between a terminal and a shell. It asks for your name and
password, checks them against /etc/passwd, changes to your home directory,
switches the process to your uid and gid, and replaces itself with your
shell.
In Version 7 the whole chain was one process taking on new programs. init
forked a getty for each terminal line listed in /etc/ttys. getty set up
the line, printed login:, and execed login with the name you typed, and
login in turn execed your shell. Your shell ended up with the process ID
init had given getty, a neat example of the fork/exec split (see
#13).
Those terminals were physical, wired to serial lines or reached by modem.
Today you usually get a pseudo-terminal from a terminal emulator or from
sshd, which does login’s checks itself.
Make is a build automation tool
that reads a Makefile describing targets, their dependencies, and the commands
to build them. Stuart Feldman wrote the first version at Bell Labs in 1976.
Before Make, building a project meant running a shell script that recompiled everything, every time. Make’s key insight is the dependency graph: each target declares what it depends on, and Make only rebuilds what’s out of date. On a large codebase, recompiling one changed file instead of the whole tree could save minutes on the hardware of the time.
The Makefile format outlived the era it was designed for. Make is still the
default build tool on most Unix systems, and its tab-indented syntax – which
Feldman famously regretted – has been tripping people up ever since.
The man(1) command
(short for “manual”) opens the reference pages that ship with the system:
commands, system calls, library functions, config files, devices. Before
the web, these pages were the documentation — and for a long time after,
they were still the fastest way to get an authoritative answer without
leaving the terminal.
The figure in the window is harder to pin down. He’s holding a scythe, which could fit: on Unix, a parent process “reaps” its children by reading their exit status, letting the kernel clear them out of the process table. Others read him as a hacker in the older sense — the clever tinkerer, not the intruder.
mbox is early Unix’s mail format: all of a user’s messages in one plain-text
file, each starting with a line that begins From , each new one appended to
the end. Because a mailbox was just a file, any tool could read it. grep
searched your mail, and system programs could notify you by appending one more
message.
Incoming mail lived in /usr/spool/mail/<username> on Version 7 (/usr/mail on
System III and V). The name mbox came from a second file: when you saved a
message you had read, mail put it in mbox in your home directory.
The simple format has one well-known cost. A body line that begins with From looks like the start of a new message, so V7’s mail wrote it as >From instead. Many mail programs still do this, which is why old archives are full
of stray > characters.
A memory leak is memory a program allocates and then forgets to release. One leak is harmless; they accumulate. Long-running processes — daemons, editors, shells open for weeks — slowly eat the machine until something swaps, slows, or dies.
Early Unix was especially exposed. C had no garbage collector, malloc and
free were entirely the programmer’s responsibility, and the machines of
the 1970s and 80s had megabytes of RAM, not gigabytes. A leaky inetd or
print spooler could crash a timesharing system overnight.
Tools like valgrind didn’t exist yet; you found leaks by reading code
and watching ps.
nroff — short for “new roff” —
is a text-formatting program that produces output suited to fixed-width
printers and terminals. It’s the engine that still renders Unix man
pages today.
The initials J.F.O. on the poster stand for Joseph Frank Ossanna,
who wrote the original nroff at Bell Labs in the early 1970s for
Research Unix. It descends from roff, which itself descends from
RUNOFF. Ossanna later
extended nroff into troff (see #17) to drive
phototypesetters with multiple fonts and proportional spacing.
/dev/null is a special device
that accepts any data written to it and returns success without storing
a byte. Reading from it returns end-of-file immediately. It’s the bit
bucket.
It shows Unix’s “everything is a file” idea at its simplest: the
same write(2) call you’d use on a regular file works here, so every
tool that can produce output can also discard it without special-casing
anything. The 2>/dev/null idiom — “throw the error messages away” — is
one of the shortest useful incantations in the shell.
Unix folklore. The story, as told by Sarah Groves Hobart on comp.unix.wizards:
The oregano is reputedly referring to an incident in which one of the original folks involved with BSD was hassled for coming across the Canadian/U.S. border with a bag of what was assumed to be an illegal substance, and turned out to be oregano.
Whether it actually happened or is just a good story that stuck, nobody seems to know. Either way, it’s on the poster.
The liquid sloshing out of the shell looks like a visual pun on buffer overflows. A buffer overflow happens when a program writes past the end of a fixed-size memory region, spilling data into whatever sits next door.
There are two flavors that matter in practice. Stack overflows hit
local variables and, critically, the saved return address of the
current function. Overwrite that, and when the function returns control
jumps wherever the attacker wants — that’s the classic 1988 Morris worm
trick against fingerd, and the basis of decades of exploits. Heap
overflows hit malloc’d memory and corrupt allocator metadata or
adjacent objects; harder to weaponize but just as damaging.
C has no bounds checking, which is what makes this easy to get wrong. Modern mitigations — stack canaries, ASLR, non-executable stacks, safer string functions — make exploitation harder but don’t eliminate the class. See Buffer overflow for the long version.
Pipes let you connect
the output of one command to the input of another using the |
character. Instead of saving intermediate results to files, you chain
commands together: ls | grep txt | wc -l. Each program reads from the
previous one’s output and writes to the next one’s input.
Doug McIlroy pushed for pipes at Bell Labs, and they became one of the ideas that defined the Unix philosophy: write small programs that do one thing, then combine them.
pwd – “print
working directory” – tells you where you are in the filesystem. Every process
has a current directory, and every relative path starts from it.
The Version 7 kernel kept that directory as an inode, not a path, so pwd had
to work the name out. It stepped into .. again and again, searching each
parent for the entry that matched the directory it had just left, until it
reached /. Then it printed the collected names in reverse.
The current directory also explains why cd is built into the shell. Dennis
Ritchie
recalled
that when fork arrived, chdir stopped working: the command changed the
directory of the child process created to run it, and that child promptly
exited. pwd can be a separate program; cd can’t.
Today pwd is usually a shell builtin that remembers the path you walked,
symlinks and all. pwd -P resolves them.
root is the superuser — uid 0 in the kernel’s eyes. Most permission
checks in Unix short-circuit for uid 0, which is why root can read any
file, kill any process, mount any filesystem, and bind to ports below
1024 (the “privileged” range reserved for services like SSH and HTTP).
For a long time, administering a Unix box meant logging in as root and
hoping you typed carefully. sudo changed that in the 80s by letting
named users run specific commands with elevated privileges, leaving an
audit trail and making “full root shell” a deliberate choice rather than
the default workflow. Most modern systems discourage direct root
logins entirely.
Shell scripts are what made Unix administration practical. A shell script is a text file of shell commands that runs as a program: instead of typing commands one at a time, you write them into a file and execute it.
The idea follows from the shell being an ordinary command. The 1974 Unix paper
already shows sh <tryout running a file of commands in sequence. Because the
shell knows how to run programs, redirect I/O, and connect pipes, a script gets
all of that for free. Add variables, loops, and conditionals, and you have a
real programming language tightly integrated with the operating system.
System startup, backups, log rotation, batch processing: all of it was (and still is) driven by shell scripts.
The wizard’s cloak is decorated with shell metacharacters. Each one does a different job: chaining commands, redirecting I/O, expanding variables, matching filenames. A handful of these symbols is most of what separates a GUI user from a shell user.
| Symbol | Name | Example |
|---|---|---|
% | Job control | fg %1 — foreground job 1 |
$ | Variable expansion | echo $HOME, $? (last exit code) |
> >> | Output redirection | ls > files.txt (overwrite / append) |
< | Input redirection | sort < input.txt |
* ? | Glob / wildcard | ls *.txt |
! | History expansion | !! (last command), !$ (last arg) |
[ ] | Test / conditionals | [ -f file.txt ] |
| ` | ` | Pipe |
& | Run in background | long-job & |
; | Command separator | cd /tmp; ls |
` | Command substitution | echo `date` (also $(...)) |
Put together, you get things like:
if [ -f file.txt ]; then
echo "file exists: $(wc -l < file.txt) lines"
fi
Spawning means starting a child process that runs a different program. Unix
splits it in two: fork copies the current process, then the child calls
exec to become the new program. The parent usually calls wait to collect
the child’s exit status. #13 explains why the split
matters.
POSIX also defines posix_spawn, which does both steps in one call, the way
VMS and Windows always have. It helps where fork is expensive: on hardware
without an MMU, which can’t do copy-on-write, or when the parent is so large
that even setting up copy-on-write mappings is slow.
On the poster, “spells” plays on the wizard imagery: the user as magician, the shell commands as incantations. There’s also a literal Unix command by that name.
spell is the classic
Unix spell-checker. Stephen C. Johnson wrote the original for Version 6
Unix in 1975, and Doug McIlroy rewrote it soon after. McIlroy had to fit an
English dictionary into a PDP-11 with tens of kilobytes of memory, so
he compressed the word list into a Bloom-filter-like structure of
hash codes. It was short, fast, and accurate enough to be
useful. Jon Bentley’s Programming Pearls tells the story well.
Related tools: look (binary-search a sorted word list by prefix) and
ispell / aspell (interactive checkers that replaced spell on most
systems).
/usr/spool was the staging ground for anything a slow device had to
work through at its own pace: print jobs queued for the line printer,
outgoing mail waiting for delivery, UUCP transfers held until the next
dial-up. On modern systems this directory has moved to /var/spool, but
the pattern hasn’t changed.
“Spool” is often said to be a backronym for Simultaneous Peripheral Operations On-Line, likely coined after the word was already in use. The underlying idea is older than Unix: buffer work to disk so the slow device doesn’t hold up the fast one.
su — short for
“substitute user” or “switch user” — starts a new shell running as a
different user. With no argument it switches to root, which for a long
time was the way to become the superuser on a Unix box. Most systems
today prefer sudo, which scopes elevation to a single command instead
of handing you a full root shell (see #25).
tar – tape archive – was built
for magnetic tape drives, which are strictly sequential: you write from one end to
the other with no random access. That constraint shaped the format permanently. tar
concatenates files one after another with a small header in front of each, and
that’s it. No index, no directory at the front. To list the contents of a
.tar.gz you have to read the whole thing from the beginning.
It shipped with Version 7 Unix in 1979, replacing tp (which had replaced tap).
The format is almost unchanged since then, which is why people still reach
for it today.
Most people type tar xvzf archive.tar.gz without thinking about the flags:
extract, verbose, gzip, file.
The T-shaped pipe junctions in the poster may reference the
tee command. tee reads
from standard input and writes to both standard output and one or more files at
the same time, like a T-junction splitting a flow of water.
This is handy for debugging pipelines (you can tap into the middle of a chain
to see what’s flowing through) or for logging (save a copy of the data while
still passing it along): make 2>&1 | tee build.log.
Dennis Ritchie created C at Bell Labs in the early 1970s, and Unix was rewritten in it shortly after. Before that, Unix was written in PDP-7 and later PDP-11 assembly, which meant it only ran on PDP machines. Rewriting it in C made it possible to port Unix to other hardware. You just needed a C compiler for the target platform. That portability is a big reason Unix spread through universities and eventually into commercial use.
Signals (also called “traps”) are short, asynchronous notifications the kernel delivers to a process: “your child exited”, “the user hit Ctrl-C”, “your terminal went away”. A program can install a handler for most of them, or let the kernel’s default action happen.
A few that come up constantly:
kill <pid>. Also polite, also
catchable. systemd, init, and orchestration tools send SIGTERM
first and wait.kill -9. Cannot be caught, blocked, or ignored. The
kernel stops the process immediately. Use when nothing else works.The shell’s trap builtin lets scripts install their own handlers:
trap 'rm -f "$tmpfile"' EXIT INT TERM
That line guarantees the temp file gets cleaned up whether the script finishes normally or gets interrupted.
Troff is the typesetter in Unix’s document-processing pipeline, and text formatting is how Unix found its first real users. To justify buying a PDP-11, the Unix group at Bell Labs promised the patents department a system for preparing patent applications. The machine arrived, the patent department adopted it, and Unix had users outside the research lab.
When Bell Labs bought a Graphic Systems CAT phototypesetter, Joe Ossanna
extended his nroff (see #24) to drive it, with
multiple fonts and proportional spacing. The name stands for “typesetter roff”.
Its output was good enough that reviewers sometimes assumed troff manuscripts
had already been published.
After Ossanna died in 1977, Brian Kernighan rewrote troff to produce
device-independent output that a small driver could translate for any printer.
GNU’s reimplementation, groff, still renders the man pages on most systems.
uucp (Unix-to-Unix Copy) connected Unix
machines years before most of them could reach the internet. It was a suite of
programs for copying files between systems over phone lines using modems. Mike
Lesk wrote the first version at Bell Labs in 1976, and it shipped with Version 7
Unix in 1979.
UUCP carried Usenet and early email between sites. Machines dialed each other
on a schedule, exchanged queued files and messages, then hung up. It wasn’t
fast, but mail could cross the country one phone call at a time, routed by
“bang paths” like foovax!barbox!user that named every hop.
wall — “write to
all” — prints a message on every terminal currently logged into the
system. It reads from a file or stdin and pushes the contents out to
everyone.
The classic use is from root, right before a shutdown:
echo "System going down in 5 minutes. Save your work." | wall
On a multi-user timesharing machine in the 80s, that was how you gave people a heads-up before pulling the plug.
whoami prints the name of the user
the current process runs as. Bill Joy added it in 2.9BSD as a short form of
who am i, but today the two answer different questions, and the difference
shows up right after su.
who am i looks your terminal up in the login records, so it reports who
logged in. whoami asks the kernel for the process’s effective user ID. After
su, they disagree:
$ su
# who am i
alice pts/0 ...
# whoami
root
This is the UNIX Magic Poster, originally created by Gary Overacre in the mid-1980s and published by UniTech Software, Inc. It was later seen on display at a USENIX Conference.
Unix has been a big part of my career from the start. My first exposure was in college, writing most of my first-year programming assignments on terminals connected to an HP-UX server. Coming from DOS and Windows, the simplicity and power of Unix hit me right away.
That experience changed how I thought about computers. Unix has been part of my computing life ever since. This project is my way of celebrating that.
Contributions welcome. When adding an annotation, try to frame it in the context of Unix's early days: how did this functionality compare to other systems at the time? What made it special? This project isn't just about explaining what things are, but why they mattered — technically and culturally.
Thanks to Andrew Tanenbaum for pointing out that threads were not available in early Unix, and for sharing Rob Pike's Unix quiz.
$ ./enjoy -- drio



The shell gets a prominent spot on the poster because it is the center of how you use Unix: you type commands, launch programs, chain them together with pipes, and tell the system what to do. What set it apart is that the shell is an ordinary program, not part of the kernel, so anyone could write a better one. In their 1974 paper, Ritchie and Thompson put it this way: because the shell runs as “an ordinary, swappable user program”, it “may be made as powerful as desired at little cost.”
People did write better ones. The first Unix shell was the
Thompson shell (sh), written
by Ken Thompson for the earliest versions of Unix in 1971. It handled command
execution, redirection, and pipes, but wasn’t a real programming language. The
Mashey shell and Bill Joy’s
csh came next, both adding scripting
features (see #14). Then in 1979, Stephen
Bourne’s sh shipped with
Version 7 Unix and became the template everything else (ksh, bash, zsh)
built on.
/dev/null is a special device
that accepts any data written to it and returns success without storing
a byte. Reading from it returns end-of-file immediately. It’s the bit
bucket.
It shows Unix’s “everything is a file” idea at its simplest: the
same write(2) call you’d use on a regular file works here, so every
tool that can produce output can also discard it without special-casing
anything. The 2>/dev/null idiom — “throw the error messages away” — is
one of the shortest useful incantations in the shell.
Unix folklore. The story, as told by Sarah Groves Hobart on comp.unix.wizards:
The oregano is reputedly referring to an incident in which one of the original folks involved with BSD was hassled for coming across the Canadian/U.S. border with a bag of what was assumed to be an illegal substance, and turned out to be oregano.
Whether it actually happened or is just a good story that stuck, nobody seems to know. Either way, it’s on the poster.
tar – tape archive – was built
for magnetic tape drives, which are strictly sequential: you write from one end to
the other with no random access. That constraint shaped the format permanently. tar
concatenates files one after another with a small header in front of each, and
that’s it. No index, no directory at the front. To list the contents of a
.tar.gz you have to read the whole thing from the beginning.
It shipped with Version 7 Unix in 1979, replacing tp (which had replaced tap).
The format is almost unchanged since then, which is why people still reach
for it today.
Most people type tar xvzf archive.tar.gz without thinking about the flags:
extract, verbose, gzip, file.
fork(2) is how Unix
makes a new process: the kernel duplicates the calling process, and now
there are two of them running the same code. They only differ in the
return value of fork itself — zero in the child, the child’s PID in the
parent — which is how each copy knows who it is.
What made this radical was how cheap it could be. Early
Unix leaned on it for everything, and modern kernels use copy-on-write so
the duplicated address space costs almost nothing until one side writes
to it. The shell runs every command by forking and then calling exec
in the child to replace itself with the target program. That fork/exec
split is unusual — VMS and Windows went with a single “spawn” call that
creates a process already running a different program — but it’s what
lets the shell set up redirections, pipes, and environment changes in
the child after fork and before exec, using ordinary code. Most of
Unix’s composability comes from that split.
Melvin Conway described the idea under the name “fork” in A Multiprocessor System Design in the early 1960s, years before Unix existed.
Shell scripts are what made Unix administration practical. A shell script is a text file of shell commands that runs as a program: instead of typing commands one at a time, you write them into a file and execute it.
The idea follows from the shell being an ordinary command. The 1974 Unix paper
already shows sh <tryout running a file of commands in sequence. Because the
shell knows how to run programs, redirect I/O, and connect pipes, a script gets
all of that for free. Add variables, loops, and conditionals, and you have a
real programming language tightly integrated with the operating system.
System startup, backups, log rotation, batch processing: all of it was (and still is) driven by shell scripts.
AWK is a tiny programming language aimed squarely at text: read input line by line, match patterns, run actions, emit output. Great on its own, better inside a pipeline.
It was written at Bell Labs in the 1970s. The name comes from the three authors’ initials: Alfred Aho, Peter Weinberger, and Brian Kernighan — the same “k” as in #5.
/usr/spool was the staging ground for anything a slow device had to
work through at its own pace: print jobs queued for the line printer,
outgoing mail waiting for delivery, UUCP transfers held until the next
dial-up. On modern systems this directory has moved to /var/spool, but
the pattern hasn’t changed.
“Spool” is often said to be a backronym for Simultaneous Peripheral Operations On-Line, likely coined after the word was already in use. The underlying idea is older than Unix: buffer work to disk so the slow device doesn’t hold up the fast one.
Troff is the typesetter in Unix’s document-processing pipeline, and text formatting is how Unix found its first real users. To justify buying a PDP-11, the Unix group at Bell Labs promised the patents department a system for preparing patent applications. The machine arrived, the patent department adopted it, and Unix had users outside the research lab.
When Bell Labs bought a Graphic Systems CAT phototypesetter, Joe Ossanna
extended his nroff (see #24) to drive it, with
multiple fonts and proportional spacing. The name stands for “typesetter roff”.
Its output was good enough that reviewers sometimes assumed troff manuscripts
had already been published.
After Ossanna died in 1977, Brian Kernighan rewrote troff to produce
device-independent output that a small driver could translate for any printer.
GNU’s reimplementation, groff, still renders the man pages on most systems.
B is the step between BCPL and C. Ken Thompson wrote it at Bell Labs around 1969 for the early Unix work on the PDP-7, with contributions from Dennis Ritchie. It borrowed heavily from BCPL — the name is widely taken to be a contraction — and dropped most of BCPL’s complexity to fit on a machine with very little memory.
B was typeless: everything was a machine word. That worked on the PDP-7 but fell apart on the PDP-11, which cared about byte-sized data. Ritchie added types, kept most of the syntax, and called the result C. B didn’t survive the transition, and today it exists mostly as a footnote in the lineage BCPL → B → C.
cat takes its name from
(con)catenate. Give it one file and it dumps the contents to stdout;
give it several and it glues them together. It was part of Version 1
Unix, written by Ken Thompson and Dennis Ritchie.
cat is also where the “Useless Use of Cat” joke comes from:
cat file | grep foo # UUOC
grep foo file # same thing, one less process
The man(1) command
(short for “manual”) opens the reference pages that ship with the system:
commands, system calls, library functions, config files, devices. Before
the web, these pages were the documentation — and for a long time after,
they were still the fastest way to get an authoritative answer without
leaving the terminal.
The figure in the window is harder to pin down. He’s holding a scythe, which could fit: on Unix, a parent process “reaps” its children by reading their exit status, letting the kernel clear them out of the process table. Others read him as a hacker in the older sense — the clever tinkerer, not the intruder.
uucp (Unix-to-Unix Copy) connected Unix
machines years before most of them could reach the internet. It was a suite of
programs for copying files between systems over phone lines using modems. Mike
Lesk wrote the first version at Bell Labs in 1976, and it shipped with Version 7
Unix in 1979.
UUCP carried Usenet and early email between sites. Machines dialed each other
on a schedule, exchanged queued files and messages, then hung up. It wasn’t
fast, but mail could cross the country one phone call at a time, routed by
“bang paths” like foovax!barbox!user that named every hop.
I have to admit, this object looks more like a boot than a sock. But it’s hard to believe the artist would leave out sockets, given how central they are to Unix and operating systems generally. So I see two readings:
See the Wikipedia entry on Berkeley sockets
or the socket(2)
man page for the API itself.
Make is a build automation tool
that reads a Makefile describing targets, their dependencies, and the commands
to build them. Stuart Feldman wrote the first version at Bell Labs in 1976.
Before Make, building a project meant running a shell script that recompiled everything, every time. Make’s key insight is the dependency graph: each target declares what it depends on, and Make only rebuilds what’s out of date. On a large codebase, recompiling one changed file instead of the whole tree could save minutes on the hardware of the time.
The Makefile format outlived the era it was designed for. Make is still the
default build tool on most Unix systems, and its tab-indented syntax – which
Feldman famously regretted – has been tripping people up ever since.
Spawning means starting a child process that runs a different program. Unix
splits it in two: fork copies the current process, then the child calls
exec to become the new program. The parent usually calls wait to collect
the child’s exit status. #13 explains why the split
matters.
POSIX also defines posix_spawn, which does both steps in one call, the way
VMS and Windows always have. It helps where fork is expensive: on hardware
without an MMU, which can’t do copy-on-write, or when the parent is so large
that even setting up copy-on-write mappings is slow.
nroff — short for “new roff” —
is a text-formatting program that produces output suited to fixed-width
printers and terminals. It’s the engine that still renders Unix man
pages today.
The initials J.F.O. on the poster stand for Joseph Frank Ossanna,
who wrote the original nroff at Bell Labs in the early 1970s for
Research Unix. It descends from roff, which itself descends from
RUNOFF. Ossanna later
extended nroff into troff (see #17) to drive
phototypesetters with multiple fonts and proportional spacing.
root is the superuser — uid 0 in the kernel’s eyes. Most permission
checks in Unix short-circuit for uid 0, which is why root can read any
file, kill any process, mount any filesystem, and bind to ports below
1024 (the “privileged” range reserved for services like SSH and HTTP).
For a long time, administering a Unix box meant logging in as root and
hoping you typed carefully. sudo changed that in the 80s by letting
named users run specific commands with elevated privileges, leaving an
audit trail and making “full root shell” a deliberate choice rather than
the default workflow. Most modern systems discourage direct root
logins entirely.
date prints or
sets the system time. Under the hood, Unix stores time as a single
count — seconds since 00:00:00 UTC on 1 January 1970, the “Unix epoch”.
The developers picked it because it was recent and round, which made it
easy to reason about.
A signed 32-bit seconds counter overflows at 03:14:07 UTC on
19 January 2038. That’s the Y2038 problem, and it’s still live: many
embedded systems and filesystems store 32-bit time_t. Modern Unixes use a
64-bit time_t. It buys around 292 billion years of headroom, which is
probably enough.
whoami prints the name of the user
the current process runs as. Bill Joy added it in 2.9BSD as a short form of
who am i, but today the two answer different questions, and the difference
shows up right after su.
who am i looks your terminal up in the login records, so it reports who
logged in. whoami asks the kernel for the process’s effective user ID. After
su, they disagree:
$ su
# who am i
alice pts/0 ...
# whoami
root
pwd – “print
working directory” – tells you where you are in the filesystem. Every process
has a current directory, and every relative path starts from it.
The Version 7 kernel kept that directory as an inode, not a path, so pwd had
to work the name out. It stepped into .. again and again, searching each
parent for the entry that matched the directory it had just left, until it
reached /. Then it printed the collected names in reverse.
The current directory also explains why cd is built into the shell. Dennis
Ritchie
recalled
that when fork arrived, chdir stopped working: the command changed the
directory of the child process created to run it, and that child promptly
exited. pwd can be a separate program; cd can’t.
Today pwd is usually a shell builtin that remembers the path you walked,
symlinks and all. pwd -P resolves them.
mbox is early Unix’s mail format: all of a user’s messages in one plain-text
file, each starting with a line that begins From , each new one appended to
the end. Because a mailbox was just a file, any tool could read it. grep
searched your mail, and system programs could notify you by appending one more
message.
Incoming mail lived in /usr/spool/mail/<username> on Version 7 (/usr/mail on
System III and V). The name mbox came from a second file: when you saved a
message you had read, mail put it in mbox in your home directory.
The simple format has one well-known cost. A body line that begins with From looks like the start of a new message, so V7’s mail wrote it as >From instead. Many mail programs still do this, which is why old archives are full
of stray > characters.
Pipes let you connect
the output of one command to the input of another using the |
character. Instead of saving intermediate results to files, you chain
commands together: ls | grep txt | wc -l. Each program reads from the
previous one’s output and writes to the next one’s input.
Doug McIlroy pushed for pipes at Bell Labs, and they became one of the ideas that defined the Unix philosophy: write small programs that do one thing, then combine them.
login is the step between a terminal and a shell. It asks for your name and
password, checks them against /etc/passwd, changes to your home directory,
switches the process to your uid and gid, and replaces itself with your
shell.
In Version 7 the whole chain was one process taking on new programs. init
forked a getty for each terminal line listed in /etc/ttys. getty set up
the line, printed login:, and execed login with the name you typed, and
login in turn execed your shell. Your shell ended up with the process ID
init had given getty, a neat example of the fork/exec split (see
#13).
Those terminals were physical, wired to serial lines or reached by modem.
Today you usually get a pseudo-terminal from a terminal emulator or from
sshd, which does login’s checks itself.
On the poster, “spells” plays on the wizard imagery: the user as magician, the shell commands as incantations. There’s also a literal Unix command by that name.
spell is the classic
Unix spell-checker. Stephen C. Johnson wrote the original for Version 6
Unix in 1975, and Doug McIlroy rewrote it soon after. McIlroy had to fit an
English dictionary into a PDP-11 with tens of kilobytes of memory, so
he compressed the word list into a Bloom-filter-like structure of
hash codes. It was short, fast, and accurate enough to be
useful. Jon Bentley’s Programming Pearls tells the story well.
Related tools: look (binary-search a sorted word list by prefix) and
ispell / aspell (interactive checkers that replaced spell on most
systems).
The curses
library hides the ugly details of moving the cursor around a terminal and
repainting regions of the screen. Before curses, programs that wanted to
do anything more than scroll text had to emit raw escape sequences for
whatever terminal they were running on — and every terminal model spoke
a slightly different dialect. Curses reads the terminal’s capabilities
from termcap/terminfo and gives you a portable API.
It’s why vi, less, top, htop, mutt, and many other TUI
programs look and work the same across terminals. The name is a pun on
“cursor optimization”. Ken Arnold wrote the original curses for BSD Unix.
diff compares two files line by line and
shows what was added, removed, or changed. Doug McIlroy and James Hunt wrote it at
Bell Labs, and it shipped with Fifth Edition Unix in 1974. Its output, the patch,
became a universal unit of change.
Before diff, reconciling two versions of a source file meant reading them side by
side. diff automated that, and once Larry Wall’s
patch(1) arrived in 1985,
you could mail someone a diff and they could apply it to their own copy of the
file.
Hunt and McIlroy described the algorithm in a 1976 paper. It finds the longest common subsequence of the two files’ lines, which is expensive to compute naively, and they made it fast enough for the hardware of the time. Most tools today use Eugene Myers’s 1986 algorithm instead; it’s the default in both GNU diff and git.
Signals (also called “traps”) are short, asynchronous notifications the kernel delivers to a process: “your child exited”, “the user hit Ctrl-C”, “your terminal went away”. A program can install a handler for most of them, or let the kernel’s default action happen.
A few that come up constantly:
kill <pid>. Also polite, also
catchable. systemd, init, and orchestration tools send SIGTERM
first and wait.kill -9. Cannot be caught, blocked, or ignored. The
kernel stops the process immediately. Use when nothing else works.The shell’s trap builtin lets scripts install their own handlers:
trap 'rm -f "$tmpfile"' EXIT INT TERM
That line guarantees the temp file gets cleaned up whether the script finishes normally or gets interrupted.
The wizard’s cloak is decorated with shell metacharacters. Each one does a different job: chaining commands, redirecting I/O, expanding variables, matching filenames. A handful of these symbols is most of what separates a GUI user from a shell user.
| Symbol | Name | Example |
|---|---|---|
% | Job control | fg %1 — foreground job 1 |
$ | Variable expansion | echo $HOME, $? (last exit code) |
> >> | Output redirection | ls > files.txt (overwrite / append) |
< | Input redirection | sort < input.txt |
* ? | Glob / wildcard | ls *.txt |
! | History expansion | !! (last command), !$ (last arg) |
[ ] | Test / conditionals | [ -f file.txt ] |
| ` | ` | Pipe |
& | Run in background | long-job & |
; | Command separator | cd /tmp; ls |
` | Command substitution | echo `date` (also $(...)) |
Put together, you get things like:
if [ -f file.txt ]; then
echo "file exists: $(wc -l < file.txt) lines"
fi
The liquid sloshing out of the shell looks like a visual pun on buffer overflows. A buffer overflow happens when a program writes past the end of a fixed-size memory region, spilling data into whatever sits next door.
There are two flavors that matter in practice. Stack overflows hit
local variables and, critically, the saved return address of the
current function. Overwrite that, and when the function returns control
jumps wherever the attacker wants — that’s the classic 1988 Morris worm
trick against fingerd, and the basis of decades of exploits. Heap
overflows hit malloc’d memory and corrupt allocator metadata or
adjacent objects; harder to weaponize but just as damaging.
C has no bounds checking, which is what makes this easy to get wrong. Modern mitigations — stack canaries, ASLR, non-executable stacks, safer string functions — make exploitation harder but don’t eliminate the class. See Buffer overflow for the long version.
The T-shaped pipe junctions in the poster may reference the
tee command. tee reads
from standard input and writes to both standard output and one or more files at
the same time, like a T-junction splitting a flow of water.
This is handy for debugging pipelines (you can tap into the middle of a chain
to see what’s flowing through) or for logging (save a copy of the data while
still passing it along): make 2>&1 | tee build.log.
The tree the wizard is working on probably stands for the Unix filesystem
hierarchy. Unix organizes files and directories as a tree rooted at /,
branching into subdirectories like /usr, /bin, and /etc. You navigate it with commands like cd, ls, and pwd.
The branching form could also represent process trees. Every process in Unix
has a parent, forming a tree rooted at init (PID 1).
A memory leak is memory a program allocates and then forgets to release. One leak is harmless; they accumulate. Long-running processes — daemons, editors, shells open for weeks — slowly eat the machine until something swaps, slows, or dies.
Early Unix was especially exposed. C had no garbage collector, malloc and
free were entirely the programmer’s responsibility, and the machines of
the 1970s and 80s had megabytes of RAM, not gigabytes. A leaky inetd or
print spooler could crash a timesharing system overnight.
Tools like valgrind didn’t exist yet; you found leaks by reading code
and watching ps.
The poster’s title is set in big block letters, much like the output of the
banner command. banner takes a
string and prints it in oversized letters built from ordinary characters. On a
shared line printer that was practical: each job started with a separator page
carrying the owner’s name in letters big enough to spot in a pile of fanfold
paper.
Two versions survive. System V’s banner prints horizontally at a fixed size.
The BSD one, by Mary Ann Horton at Berkeley, prints sideways down the page, so
the message can run as long as the paper does. FIGlet does the same job today
with many more fonts.
wall — “write to
all” — prints a message on every terminal currently logged into the
system. It reads from a file or stdin and pushes the contents out to
everyone.
The classic use is from root, right before a shutdown:
echo "System going down in 5 minutes. Save your work." | wall
On a multi-user timesharing machine in the 80s, that was how you gave people a heads-up before pulling the plug.
Three sets of initials, three people who shaped Unix:
ken, which is how he’s cited in most early Unix
source.awk
(see #15).Dennis Ritchie created C at Bell Labs in the early 1970s, and Unix was rewritten in it shortly after. Before that, Unix was written in PDP-7 and later PDP-11 assembly, which meant it only ran on PDP machines. Rewriting it in C made it possible to port Unix to other hardware. You just needed a C compiler for the target platform. That portability is a big reason Unix spread through universities and eventually into commercial use.
When two processes talk over a pipe, one is producing data and the other
is consuming it. The kernel holds a small buffer in between. If the
producer gets ahead, the buffer fills, and the kernel blocks the
producer’s next write until the consumer drains some space. That’s
backpressure: the slow side telling the fast side to wait.
Whether the valve on the poster’s pipe is a deliberate nod to this or just a nice bit of plumbing, I’ll leave to you.
Daemons are long-running
background processes, usually started at boot, that do the system’s chores:
cron runs scheduled jobs, syslogd collects log messages, sshd accepts
logins. On Unix they are ordinary processes, not part of the kernel, so you can
stop, restart, or replace a service without rebooting the machine.
The name comes from MIT’s Project MAC around 1963. Fernando Corbató’s team
borrowed it from Maxwell’s demon, the imaginary being in physics that quietly
sorts molecules, for background processes that work tirelessly on system
chores. Unix daemons usually advertise it with a trailing d.
su — short for
“substitute user” or “switch user” — starts a new shell running as a
different user. With no argument it switches to root, which for a long
time was the way to become the superuser on a Unix box. Most systems
today prefer sudo, which scopes elevation to a single command instead
of handing you a full root shell (see #25).