Understand what happens between pressing Enter and seeing output. Each lesson starts from a small, concrete goal - parse this line, run that program - and ends with it working, until your shell runs pipelines and background jobs.
Over the next 34 lessons you build a working Unix shell from scratch - the program that sits between you and the operating system, turning a line of text into running processes. You start with a read-eval-print loop, then learn to launch external programs with fork and exec, add the builtins a shell has to run itself (cd, pwd, exit), and grow a word parser that handles quoting, variables, and filename globbing.
From there you wire up the machinery that makes a shell feel like a shell: redirection with `>`, `<`, and `2>&1`, multi-stage pipelines with `|`, and the control operators `;`, `&&`, `||`, and background `&`. You finish with a real, runnable shell you can drive interactively or feed a script. It is a teaching-grade shell - clear and correct on the core POSIX behaviors, and deliberately stopping short of command substitution, here-documents, arithmetic, and interactive job control - and every lesson ends with the shell running.
Every shell is a loop - print a prompt, read a line, act on it, repeat. Today you build that loop and the way it ends, which is the skeleton every later lesson hangs code on.
Print a prompt, read one line of input at a time, and stop cleanly at end of input.
A shell is a read-eval-print loop: it prints a prompt, reads a line you type, does what the line says, and loops back for the next one. Today only the “read” and “loop” halves exist - there is nothing to evaluate yet - but this loop is the spine of the whole project, so it is worth getting the shape right.
The one subtlety is how the loop ends. When input runs out - the user presses
Ctrl-D at the terminal, or a script file reaches its last line - the read
returns an end-of-input signal rather than a line. That is your cue to leave the
loop and exit 0. Strip the trailing newline from each line as you read it; every
later lesson assumes the line it receives has no \n on the end.
// print a prompt, read a line, repeat until EOFfor (;;) {printf("$ ");if (!read_line(&line)) break; // EOF -> stop// (nothing to do with the line yet)}
It's a genuinely usable POSIX-ish shell for everyday external commands, pipelines, redirection, and scripting, but it stops well short of a full shell: no command substitution, here-docs, arithmetic, subshells, or job control, and builtins can't run inside a pipeline stage.
The definitive reference for the process, signal, and file-descriptor APIs a shell is built on.
A more modern, exhaustive companion covering fork/exec, pipes, and job control in detail.
The standard that defines the shell grammar, quoting, and expansion rules this project targets.
Free online text with clear chapters on processes and the fork/exec API that ground what a shell orchestrates.