sh (Unix Shell)
The Unix shell command interpreter, first written by Ken Thompson in 1971 and standardized by POSIX, whose canonical name "sh" still identifies the portable shell on every Unix-like system.
Created by Ken Thompson (original sh); Stephen R. Bourne (Bourne shell)
sh is the canonical name of the Unix shell—the command-line interpreter and scripting language that has carried that two-letter name since Ken Thompson wrote the first Unix shell in 1971. More than any single program, sh is an interface: a standardized command language that any conforming implementation can provide. Over five decades, the binary behind /bin/sh has changed many times—from the original Thompson shell, to Stephen Bourne’s far more capable Bourne shell, to today’s POSIX-conformant implementations such as Dash and BusyBox ash—but the name, the invocation, and the core scripting conventions have endured. When developers speak of “writing a shell script that runs anywhere,” they almost always mean writing for sh.
This page treats sh as the through-line of Unix shell history and the portable standard it became. For the specific 1979 implementation by Stephen Bourne, see the dedicated Bourne Shell page; for the formal standard as a topic in its own right, see POSIX Shell.
History & Origins
The First Unix Shell (1971)
The first program named sh was the Thompson shell, written single-handedly by Ken Thompson for the First Edition of Unix at Bell Labs in 1971. It was a small, elegant command interpreter rather than a programming language: it read commands, located the corresponding programs, and ran them. Even so, it established conventions that survive today, most importantly a compact syntax for input/output redirection that let any command’s input or output be attached to a file. Filename expansion (globbing) was handled by a separate helper program called glob, and rudimentary flow control was available through if and goto as external commands.
The Thompson shell shipped as the default sh through roughly Versions 1–6 of Research Unix. Its design reflected Unix’s founding philosophy: keep each tool simple and let users compose tools together. That philosophy was reinforced in 1973 when pipes—Doug McIlroy’s long-advocated idea—were added to Unix, allowing the output of one command to flow directly into the next. The pipeline (|) became, and remains, the defining idiom of shell programming.
From Command Interpreter to Programming Language
The Thompson shell’s great limitation was that it was never meant for serious scripting. It lacked named variables, its control flow relied on external commands, and a script file consumed standard input—which made it impossible to use a script as a stage in a pipeline. The first steps toward a programmable shell came from the PWB/Mashey shell (John Mashey, around 1977) for the Programmer’s Workbench Unix, which folded control structures like if, while, and switch into the shell itself.
The decisive change arrived in 1979, when Stephen R. Bourne’s shell replaced the Thompson shell as /bin/sh in Unix Version 7. The Bourne shell introduced named variables, structured control flow built into the shell, here-documents, command substitution, signal trapping, and—critically—the separation of script input from standard input that made shell scripts usable as pipeline filters. From this point forward, “sh” effectively meant “the Bourne shell and its language,” and that language became the reference for everything that followed.
A Family of Compatible Shells
Because sh was a name and a convention as much as a program, a family of implementations grew up around it, each aiming to be sh-compatible while adding its own strengths:
- The C shell (
csh, 1979) offered interactive conveniences and a C-like syntax, though it was a separate lineage rather than anshsuccessor. - The Korn shell (
ksh, David Korn, 1983) was a strict superset of the Bourne shell with arrays, arithmetic, and command-line editing; it became the default on several commercial Unix systems. - Bash (the GNU Bourne-Again Shell, Brian Fox, 1989) provided a free,
sh-compatible replacement that became the de facto interactive shell of Linux. - The Almquist shell (
ash) and its descendants—Dash on Debian/Ubuntu and BusyBox ash on embedded and container systems—provided small, fast, strictly POSIX implementations ideal for the/bin/shrole.
Design Philosophy
The enduring idea behind sh is that the shell is a glue language for programs, not a self-contained application platform. Its design rewards composition over monoliths:
Everything is a command. Programs, built-ins, and control structures are invoked the same way and connected through the same plumbing. A shell script reads as a sequence of commands, and any command can be replaced by another program with the same interface.
Pipelines and redirection are the core abstraction. The | operator and the >, >>, and < redirections let small tools be assembled into larger workflows without any tool knowing about the others. This is the most direct expression of the Unix philosophy.
Text is the universal interface. Commands communicate through streams of text on standard input, output, and error. The shell does not impose data structures; it moves bytes between programs.
Portability through standardization. Once codified by POSIX, sh became a contract. A script that uses only POSIX sh features can be expected to run on any conforming system, regardless of which shell binary actually sits behind /bin/sh.
Key Features
Variables and Parameter Expansion
| |
Control Structures
| |
Pipelines and Redirection
| |
Command Substitution
| |
Functions and Signal Handling
| |
Evolution: One Name, Many Implementations
A defining characteristic of sh is that the program behind the name has changed repeatedly while the interface remained stable. The table below summarizes the major implementations that have served as /bin/sh:
| Implementation | Era | Role |
|---|---|---|
| Thompson shell | 1971–1979 | Original sh; minimal command interpreter |
| Bourne shell | 1979–1990s | The programmable sh; reference for the language |
| Korn shell (ksh) | 1983– | sh-compatible superset; default on some commercial Unix |
| Bash (POSIX mode) | 1989– | /bin/sh on macOS and historically on many Linux systems |
| Dash | 1997– | Lightweight POSIX sh; /bin/sh on Debian/Ubuntu |
| BusyBox ash | 1990s– | Compact /bin/sh for Alpine Linux and embedded systems |
The practical consequence is that “the sh language” is best understood as the POSIX shell command language, not any single binary. A script invoked with #!/bin/sh should use only features guaranteed by POSIX, because the actual interpreter could be Dash on one machine, Bash-in-POSIX-mode on another, and BusyBox ash on a third.
Standardization
The fragmentation of sh implementations in the 1980s created a portability problem that the POSIX standards solved. IEEE Std 1003.2-1992 ratified the shell command language, taking the System V Bourne shell as its primary reference and reconciling behavior across the V7, System V, BSD, and Korn shell lineages. In 2001, the shell standard was merged into the unified POSIX base specification (IEEE Std 1003.1-2001, also the Single UNIX Specification version 3).
The current revision is IEEE Std 1003.1-2024 (POSIX Base Specifications, Issue 8), published on 14 June 2024. Its Shell and Utilities volume defines the sh command interpreter and the standard utilities that scripts rely on. This standard is what gives the phrase “POSIX sh” a precise meaning, and it is the target most portable scripts and tools such as ShellCheck validate against.
Current Status
The sh interface is as central to computing today as it has ever been, even though almost no one runs the original Thompson or Bourne binaries. On contemporary systems, /bin/sh typically resolves to a strict POSIX implementation:
- Debian and Ubuntu:
/bin/shis Dash, chosen for fast startup and strict POSIX conformance in boot scripts. - macOS:
/bin/shis provided by Bash running in POSIX mode (Apple ships an older Bash for licensing reasons). - Alpine Linux and many embedded systems:
/bin/shis BusyBox ash, a compact POSIX shell well suited to containers and constrained devices.
Because so much infrastructure—init systems, container entrypoints, configure scripts, CI pipelines, and cloud provisioning—depends on /bin/sh, writing portable sh remains a core skill. The discipline is to use only POSIX features: prefer $( ) over backticks for nesting, avoid Bash-only constructs like [[ ]], arrays, and <<< here-strings, and test scripts against a minimal shell such as Dash.
Why sh Matters
Few names in computing have proven as durable as sh. It has identified the Unix shell continuously since 1971, surviving complete reimplementations, the rise and fall of commercial Unix, and the dominance of Linux. Its conventions—the pipeline, redirection, the $? exit status, the if/fi block pairing inherited from the Bourne shell—are written into the muscle memory of generations of programmers and into the source of countless build systems and deployment scripts.
The reason sh matters is the same reason it has lasted: it is a stable interface over a changing implementation. By separating the name and the language from any particular binary, Unix made it possible to standardize shell scripting without freezing innovation. New shells could be faster, smaller, or more feature-rich, yet still be sh. For anyone learning systems programming, understanding sh is understanding the connective tissue of Unix itself—the layer where small programs are joined into the workflows that run the world’s servers, containers, and embedded devices.
Timeline
Notable Uses & Legacy
System Initialization and Service Scripts
Unix and Linux systems have long used sh scripts for boot-time initialization—from System V rc scripts and BSD /etc/rc.d files to modern container entrypoints. Targeting plain sh maximizes the chance a script runs unchanged across distributions and Unix variants.
GNU Autoconf configure Scripts
Autoconf generates ./configure scripts that deliberately target portable sh as the lowest common denominator, so that source packages can be configured and built on any Unix-like host before a specific shell or compiler is assumed.
Container Base Images (Alpine / BusyBox)
Alpine Linux, a popular minimal base image for Docker containers, provides /bin/sh via BusyBox ash. A large share of container tooling and Dockerfile RUN steps therefore execute through a compact POSIX sh implementation rather than Bash.
CI/CD and Cloud Automation
Continuous integration platforms and cloud provisioning tools (cloud-init, package post-install scripts) frequently default to sh for setup steps, since POSIX sh is reliably present even on stripped-down or freshly bootstrapped systems.
Embedded and Resource-Constrained Systems
Routers, set-top boxes, and other embedded devices commonly ship BusyBox or Dash as /bin/sh, where a small, fast, POSIX-conformant interpreter is preferable to a feature-rich shell.
Language Influence
Influenced By
Influenced
Running Today
Run examples using the official Docker image:
docker pull bash:5.2Example usage:
docker run --rm -v $(pwd):/app -w /app bash:5.2 sh hello.sh