$unix-magic --init
loading references
drio@unixmagic:~$ ls
about github help rss
01 Shell
10 null
11 Oregano
12 tar
13 fork
14 Shell script
15 AWK
16 spool
17 troff
18 B
19 cat
02 man
20 uucp
03 Pipes
04 Memory leaks
05 dmr · kt · bwk
06 The c programming language
07 Backpressure
08 Daemons
09 su
Unix Magic Poster
21 boot (or sock?)
22 make
23 spawn
24 nroff (J.F.O.)
25 root
26 dates
27 whoami
28 pwd
29 mbox
30 login
31 spells
32 curses
33 diff
34 traps
35 Shell's symbols
36 Overflows
37 tee
38 Filesystem hierarchy
39 skull
40 banner
41 wall

man unix-magic

Name
unix-magic — interactive guide to the UNIX Magic poster
Description

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.

History

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.

Author
Poster artwork by Gary Overacre (mid-1980s), published by UniTech Software, Inc.
Site by drio.
Files
./the-view.webpGary Overacre's second poster, "The View"
./computer-feuds.webpGary Overacre's third poster, "Computer Feuds"
./puzzle.webpa 1000-piece puzzle drio had made of this poster
Contributing

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.

Acknowledgements

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
cat markers/*.md
Shell
A gateway to controlling the system

The shell gets a prominent spot on the poster because it really is the center of how you use Unix. It’s the command-line interface where you type commands, launch programs, chain them together with pipes, and generally tell the system what to do.

What eventually set the Unix shell apart was that it doubled as a programming language. You could use it interactively, but you could also write scripts to automate repetitive work — something most operating systems at the time didn’t offer.

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. Then in 1979, Stephen Bourne’s sh shipped with Version 7 Unix and became the template everything else (ksh, bash, zsh) built on.

null
The device file that throws everything away

/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’s a perfect illustration of Unix’s “everything is a file” idea: 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, and it predates the “quiet mode” flags that most programs eventually grew.

Oregano
A story about a bag of herbs at the Canadian border

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
Pack files onto tape -- and still everywhere 45 years later

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 essentially all it does. There’s 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 both a testament to its simplicity and why people still reach for it today.

The command tar xvzf archive.tar.gz is one of those things Unix users type from muscle memory without thinking about what the flags mean: extract, verbose, gzip, file.

fork
The system call that creates a new process

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 at the time 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. A lot of Unix’s composability flows from that one design choice.

Melvin Conway described the idea under the name “fork” in A Multiprocessor System Design in the early 1960s, years before Unix existed.

Shell script
The shell language

A shell script is a text file containing a sequence of shell commands that run as a program. Instead of typing commands one at a time, you write them into a file and execute it.

Shell scripts are what made Unix administration practical. System startup, backups, log rotation, batch processing - all of it was (and still is) driven by shell scripts. Because the shell already knows how to run programs, redirect I/O, and handle pipes, a script gets all of that for free. Add variables, loops, and conditionals, and you have a real programming language that’s tightly integrated with the operating system.

AWK
A small language built for text processing

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.

spool
/usr/spool — where jobs waited their turn

/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, which has the ring of something invented after the fact to justify a word everyone was already using. The underlying idea is older than Unix: buffer work to disk so the slow device doesn’t hold up the fast one.

troff
A program in formatting documents for the document processing system.

Troff is the major component of a document processing system developed by Bell Labs for the Unix operating system. Troff stands for “typesetter roff”, developed as a descendant to roff, where roff was a Unix version of one of the earliest text formatting programs called RUNOFF. A typical distribution of troff includes macros for many document styles, including ones for the Unix man pages.

B
The language that came between BCPL and C

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
Concatenate files to standard output

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
man
Reference manual viewer for commands, system calls, and files

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 is suggestive: 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
A Unix program for file copy requests.

uucp (Unix-to-Unix Copy) was a suite of programs for copying files between Unix systems over phone lines using modems. Written by Mike Lesk at Bell Labs in 1978, it was one of the earliest ways Unix machines could talk to each other.

UUCP was the backbone of Usenet and early email between sites. Machines would dial each other on a schedule, exchange queued files and messages, then hang up. It wasn’t fast, but it connected Unix systems years before the internet was widely available.

boot (or sock?)
The booting process, or maybe a Unix socket

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:

  • If it’s a boot, it points to the boot process — the small dance of firmware, boot loader, and kernel that brings the system to life.
  • If it’s a sock (a thick, oversized one), it’s a nod to Unix network sockets. Sockets arrived in 4.2BSD in 1983 and gave processes a single API for talking to each other, whether they were on the same machine (Unix-domain sockets) or across a TCP/IP network. Almost every other operating system eventually adopted the Berkeley socket interface.

See the Wikipedia entry on Berkeley sockets or the socket(2) man page for the API itself.

make
Build automation tool -- only recompile what changed

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 that distinction mattered enormously – recompiling a single 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 – a Feldman famously regretted – has been tripping people up ever since.

spawn
An operation in which a new child process is created

Spawning means creating a new child process. In Unix, this is traditionally done with fork and exec: fork creates a copy of the current process, then the child calls exec to replace itself with a different program. The parent typically calls wait to wait for the child to finish.

POSIX also defines posix_spawn, which combines the two steps into one call and can be more efficient since it avoids copying the parent’s entire address space.

nroff (J.F.O.)
A text formatter for fixed-width output; J.F.O. for its author

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
The all-powerful administrative account

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 privileged network ports (those numbered below 1024, reserved so that only trusted processes can offer services like SSH or HTTP on the well-known ports).

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.

dates
Displaying and setting the system clock

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 epoch was picked because it was close enough to when these systems were built and round enough to be 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 real — a lot of embedded systems and filesystems still use 32-bit time_t. Modern Unixes use 64-bit time_t, which buys around 292 billion years of headroom, which is probably enough.

whoami
Prints the effective user of the current shell

whoami prints the effective user name of the current process. It was added to BSD in the early 1980s. Handy after su when you’ve forgotten which identity you’re wearing.

pwd
Print the current working directory

pwd — “print working directory” — tells you where you are in the filesystem. It’s been part of Unix from the early days and is a shell builtin on most modern systems, which matters because the shell tracks the logical path (honoring symlinks you traversed) while the external /bin/pwd returns the resolved physical path. Usually they agree; when they don’t, pwd -P vs pwd -L is how you tell them apart.

mbox
The mail system format

mbox is a reference to the mail format from the early days of Unix. In the mbox format, all email messages for a user are stored in a single file, with new messages appended to the end. User mailboxes lived in /usr/mail/<username>. It’s a good example of the “everything is a file” principle. You could read your mail with standard text tools, and system notifications were just more messages appended to the same file.

Pipes
Connect command outputs to command inputs to build pipelines

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
The gateway into the system

login is the gateway into the system. It authenticates the user, initializes the environment by changing to the user’s home directory, and spawns a process running as the user (with their uid and gid), using their shell of choice.

Additionally, the standard input and output need to be attached to a terminal: this could be a pseudo-terminal (if you are in a graphical interface or using ssh), or a physical terminal (as was common at the time).

spells
The wizard's incantations — and the Unix spell-checker

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 — a lovely piece of engineering worth reading about on its own. 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 famously 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).

curses
A terminal control library for building text user interfaces

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 countless 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
Compare two files and show exactly what changed

diff compares two files line by line and shows what was added, removed, or changed. Doug McIlroy wrote it at Bell Labs in the early 1970s.

Before diff, reconciling two versions of a source file meant reading them side by side. diff automated that completely and its output format – the patch – became a universal unit of change. You could mail a patch to someone and they could apply it with patch(1) without ever seeing the original file.

The algorithm diff uses – the longest common subsequence problem – was formalized by James Hunt and McIlroy in a 1976 paper. For large files the naive approach is expensive, and getting it fast enough to be useful on the hardware of the time was non-trivial. The Hunt-McIlroy algorithm is still the basis of most diff implementations.

traps
Signals — the kernel's way of interrupting a process

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:

  • SIGINT — what Ctrl-C sends. Polite request to stop; programs usually catch it to clean up first.
  • SIGTERM — the default from kill <pid>. Also polite, also catchable. systemd, init, and orchestration tools send SIGTERM first and wait.
  • SIGKILLkill -9. Cannot be caught, blocked, or ignored. The kernel stops the process immediately. Use when nothing else works.
  • SIGHUP — originally “the terminal hung up”. Many daemons repurpose it to mean “reread your config file” because no one’s dialing in anymore.

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.

Shell's symbols
The runes on the wizard's cloak — shell metacharacters

The wizard’s cloak is decorated with shell metacharacters. Each of them gives the shell a different superpower: 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.

SymbolNameExample
%Job controlfg %1 — foreground job 1
$Variable expansionecho $HOME, $? (last exit code)
> >>Output redirectionls > files.txt (overwrite / append)
<Input redirectionsort < input.txt
* ?Glob / wildcardls *.txt
!History expansion!! (last command), !$ (last arg)
[ ]Test / conditionals[ -f file.txt ]
``Pipe
&Run in backgroundlong-job &
;Command separatorcd /tmp; ls
`Command substitutionecho `date` (also $(...))

Put together, you get things like:

if [ -f file.txt ]; then
    echo "file exists: $(wc -l < file.txt) lines"
fi

Nothing magical — it just looks that way the first time you see it.

Overflows
Buffer overflows — writing past the end of memory

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’s lack of automatic bounds checking is what makes this easy to get wrong, and Unix systems have been a primary target because they’re everywhere. Modern mitigations — stack canaries, ASLR, non-executable stacks, safer string functions — make exploitation harder but don’t eliminate the class. See Buffer overflow on Wikipedia for the long version.

tee
The T-junctions in the pipes, possibly a reference to the tee command.

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.

Filesystem hierarchy
Navigating the filesystem

The tree-like shape the wizard is manipulating is likely a reference to the Unix filesystem hierarchy. Unix organizes files and directories as a tree rooted at /, branching into subdirectories like /usr, /bin, /etc, and so on. 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).

skull
The drain where data goes to die — /dev/null

The skull-like spigot connected to the shell most likely represents /dev/null, the special Unix device that discards all data written to it. Redirecting output to /dev/null sends it into a void. Nothing comes back. The skull is a fitting symbol for where data goes to die. See also the /dev/null entry.

It could also be a nod to Unix daemons, background processes that run without a terminal. The gargoyle-like appearance of the spigot fits the daemon imagery.

Memory leaks
Memory that a program allocates and forgets to give back

A memory leak is memory a program allocates and then forgets to release. Each leak on its own is harmless; the damage comes from accumulation. Long-running processes — daemons, editors, shells open for weeks — slowly eat the machine until something swaps, slows, or dies.

This mattered a lot on early Unix. 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 bring a timesharing system to its knees overnight. Tools like valgrind didn’t exist yet; you found leaks by reading code and watching ps.

banner
A program that outputs ASCII art

The poster’s title is rendered in large block letters, much like the output of the banner command. banner takes a text string and prints it in oversized ASCII characters, originally meant for printing headers on line printers so you could tell whose printout was whose in a shared printer room. It was a common utility on Unix systems and a fun one to play with at a terminal.

wall
Broadcast a message to every logged-in user

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.

dmr · kt · bwk
The three sets of initials that built Unix

Three sets of initials, three people who shaped Unix:

  • dmr — Dennis M. Ritchie. Co-created Unix and designed the C programming language.
  • kt — Ken Thompson. Co-created Unix with Ritchie. His Bell Labs login was actually ken, which is how he’s cited in most early Unix source.
  • bwk — Brian W. Kernighan. Co-author of The C Programming Language and The Unix Programming Environment, and the “k” in awk (see #15).
The c programming language
A key programming language in the creation of Unix

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.

Backpressure
What happens when the producer is faster than the consumer

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
Service processes that run in the background and supervise the system or provide functionality to other processes

Daemons are programs that run in the background and are often started at system boot time. They respond to network requests, hardware activity, or other programs by performing some task. Daemons such as cron may also perform defined tasks at scheduled times.

su
Switch to another user's shell

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).