Skip to content
Shell & TerminalHistory Published Updated 5 min readViews unavailable

The History of the Unix Shell: From Thompson Shell to Bash and Zsh

Every shell in daily use today descends from a 1971 command interpreter, branching through PWB, Bourne, and C shell into the families still visible now.

Every shell in everyday use today — Bash, Zsh, tcsh, fish — traces back to a single, genuinely minimal starting point in 1971, branching over the following decade into two distinct family trees whose differences are still visible in how these shells behave today.

The starting point: the Thompson shell

The Thompson shell, written by Ken Thompson, shipped with the very first version of Unix in 1971. It introduced foundational concepts nearly every later shell kept — piping commands together, basic control structures, and filename globbing — but it was a simple command interpreter, not designed for serious scripting.

The first major branch: Bourne vs. C shell

Two shells emerged to address the Thompson shell’s scripting limitations, taking genuinely different approaches. Stephen Bourne, at Bell Labs, began work in 1976 on what became the Bourne shell, distributed with Unix Version 7 in 1979 — adding proper variables, control structures, and command substitution, with syntax influenced by Bourne’s background with the ALGOL 68C compiler. Separately, Bill Joy at Berkeley created the C shell (csh), distributed starting with 2BSD in 1979, deliberately designed to read more like the C programming language and to prioritize interactive use, introducing command history and aliases along the way.

Why this branch still matters today

Bash and Zsh both descend from the Bourne shell lineage; tcsh descends from Bill Joy’s C shell — two family trees with genuinely different syntax and scripting philosophies that never fully converged, which is exactly why csh-family and sh-family scripts remain mutually unreadable to someone only familiar with the other lineage.

Bash: the GNU Project’s free alternative

Bash (Bourne Again Shell), written by Brian Fox and released in 1989, was created as a free software replacement for the Bourne shell and other proprietary Unix shells, as part of the GNU Project’s broader mission — adding features like command-line editing (via the GNU Readline library, developed alongside it) and job control while remaining broadly Bourne-shell-compatible.

Zsh: born from a student project at Princeton

Zsh, created by Paul Falstad in 1990 while a student at Princeton University, combined ideas from ksh, tcsh, and other existing shells, adding especially powerful globbing, completion, and prompt customization — capabilities that eventually made it popular enough to become macOS’s default shell in 2019.

The more recent branch: shells that break from POSIX conventions deliberately

Fish, first released in 2005 by Axel Liljencrantz, took a different approach entirely — deliberately breaking POSIX compatibility in favor of more consistent, more discoverable syntax and genuinely useful defaults (like syntax highlighting and autosuggestions) out of the box, without requiring extensive configuration the way Bash or Zsh typically do.

Why this history still shapes real, everyday scripting decisions

Understanding this lineage explains why “which shell” isn’t purely an aesthetic preference: a script written in Bourne-lineage syntax fails entirely under tcsh, POSIX sh and Bash overlap substantially but diverge in specific documented ways, and Zsh’s interactive strengths come from decades of accumulated features distinct from what Bash or a strict POSIX shell provides — decisions made by Thompson, Bourne, and Joy in the 1970s are still directly responsible for compatibility boundaries every shell script author runs into today.

The gap between Thompson and Bourne: the PWB shell

The Thompson shell was not directly followed by the Bourne shell. In between, John Mashey developed the PWB shell (Programmer’s Workbench shell, also called the Mashey shell), starting around 1974-1975, as an upward-compatible extension of the Thompson shell rather than a rewrite — adding shell variables and, notably, making if and goto into real internal shell constructs, later extended into if/then/else/endif and while control structures. When Version 7 Unix shipped the from-scratch Bourne shell in 1979, it was not compatible with either the Thompson or PWB shells at the source level, but it re-implemented equivalents of most of the PWB shell’s programming features inside genuinely new syntax.

The Korn shell bridged both 1970s branches

David Korn at Bell Labs released the Korn shell (ksh) in 1983, deliberately combining the Bourne shell’s scripting compatibility with interactive conveniences the C shell family had already popularized — command history, aliases, and job control — inside one shell for the first time. ksh’s influence outlasted its own adoption: when POSIX formally standardized shell behavior as POSIX.2 in 1992, the specification drew substantially on ksh’s existing behavior, meaning “POSIX-compliant shell” today effectively means “compatible with the scripting behavior ksh had already established.”

A second Bourne-family descendant: the Almquist shell

Not every Bourne-lineage shell prioritized features over size. Kenneth Almquist wrote the Almquist shell (ash) in the late 1980s, first posted to Usenet in May 1989, as a compact, close-to-POSIX Bourne-shell clone with a deliberately small footprint. Debian’s port of ash, renamed Dash in 2002, became Ubuntu’s default /bin/sh in October 2006, specifically because a minimal, fast-starting shell measurably speeds up boot-time script execution compared to Bash — the same “which /bin/sh actually runs your script” distinction that still matters for portability today.

Fish’s most recent chapter: a language rewrite, not just a feature release

Fish’s break from POSIX conventions continued well past its 2005 debut. In 2023, its maintainers began an effort — starting, by their own account, as a joking pull request titled “rewrite it in Rust” — that became a genuine two-year migration of Fish’s entire C++ codebase to Rust, translating roughly 57,000 lines of C++ into about 75,000 lines of Rust across more than 2,600 commits from over 200 contributors. Fish 4.0 shipped as the first stable release built on that Rust codebase, motivated less by raw speed than by Rust’s safer concurrency model for features like live syntax highlighting and autosuggestions that run alongside command entry.

Why POSIX standardization didn’t end the divergence

POSIX.2 gave Bourne-family shells a common scripting target, but it neither covers csh-family shells at all nor prevents POSIX-compliant shells from adding their own extensions beyond the standard — which is exactly why Bash and Zsh remain only partially interchangeable despite both being POSIX-compliant, and why a standard finalized in 1992 did not, and was never going to, freeze shell development in place.

Related:

Sources:

Comments