Est. 1971 Beginner

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)

Paradigm Procedural, Scripting
Typing Dynamic, Weak
First Appeared 1971
Latest Version POSIX.1-2024 (IEEE Std 1003.1-2024, June 2024)

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 an sh successor.
  • 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/sh role.

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
name="World"
echo "Hello, ${name}!"          # Hello, World!

echo "Script name: $0"          # name of the script
echo "First argument: $1"       # first positional parameter
echo "Argument count: $#"       # number of arguments
echo "Exit status: $?"          # status of the last command
echo "Process ID: $$"           # PID of the shell

echo "${CONFIG:-/etc/default}"  # use default if CONFIG is unset

Control Structures

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# if / elif / else
if [ "$#" -eq 0 ]; then
    echo "No arguments given"
elif [ "$#" -eq 1 ]; then
    echo "One argument: $1"
else
    echo "$# arguments given"
fi

# case statement
case "$1" in
    start) echo "Starting..." ;;
    stop)  echo "Stopping..." ;;
    *)     echo "Usage: $0 {start|stop}" ;;
esac

# for and while loops
for f in *.log; do
    echo "Found: $f"
done

count=3
while [ "$count" -gt 0 ]; do
    echo "$count"
    count=$(( count - 1 ))
done

Pipelines and Redirection

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Classic pipeline: list users from the password file
cut -d: -f1 /etc/passwd | sort | head

# Redirection
command > out.txt          # overwrite stdout to a file
command >> out.txt         # append stdout to a file
command 2> err.txt         # redirect stderr
command > all.txt 2>&1     # combine stdout and stderr

# Here-document
cat <<EOF
line one
line two
EOF

Command Substitution

1
2
3
4
5
6
# Backtick form (portable, original)
today=`date +%Y-%m-%d`

# $( ) form (POSIX, preferred for nesting)
files=$(ls | wc -l)
echo "$files files in $today"

Functions and Signal Handling

1
2
3
4
5
6
7
8
9
log() {
    echo "[$(date +%H:%M:%S)] $*"
}

tmp=$(mktemp)
trap 'rm -f "$tmp"; log "cleaned up"' EXIT INT TERM

log "working..."
echo data > "$tmp"

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:

ImplementationEraRole
Thompson shell1971–1979Original sh; minimal command interpreter
Bourne shell1979–1990sThe 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
Dash1997–Lightweight POSIX sh; /bin/sh on Debian/Ubuntu
BusyBox ash1990s–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/sh is Dash, chosen for fast startup and strict POSIX conformance in boot scripts.
  • macOS: /bin/sh is provided by Bash running in POSIX mode (Apple ships an older Bash for licensing reasons).
  • Alpine Linux and many embedded systems: /bin/sh is 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

1971
Ken Thompson writes the first Unix shell, named sh, for First Edition Unix at Bell Labs; it introduces a compact I/O redirection syntax and works with a separate glob program for filename expansion
1973
Pipes (the | mechanism), suggested by Doug McIlroy, are added to Unix and the shell around the Third/Fourth Edition, cementing the pipeline as the core composition model
1977
The PWB/Mashey shell, written by John Mashey for the Programmer's Workbench Unix, adds built-in control structures (if, while, switch), pointing toward a programmable shell
1979
The Bourne shell, written by Stephen R. Bourne, replaces the Thompson shell as /bin/sh in Unix Version 7; the C shell (csh) also appears in BSD the same year
1983
David Korn releases the Korn shell (ksh) at Bell Labs, an sh-compatible superset that becomes the default shell on several commercial Unix systems
1989
Brian Fox releases Bash (the GNU Bourne-Again Shell) as a free, sh-compatible replacement; Kenneth Almquist's lightweight ash (Almquist shell) also dates to around this period
1992
IEEE Std 1003.2-1992 ratifies the POSIX shell command language, standardizing sh behavior on the System V Bourne shell as the portable baseline
2001
IEEE Std 1003.1-2001 (Single UNIX Specification version 3) merges the shell and utilities standard into the unified POSIX specification
2006
Ubuntu 6.10 switches /bin/sh from Bash to Dash for faster, strictly POSIX boot scripting; Debian later makes the same change its default, a pattern many distributions follow
2024
IEEE Std 1003.1-2024 (POSIX Base Specifications, Issue 8) is published on 14 June 2024, the current standard defining the sh command interpreter and utilities

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

Multics shell RUNCOM ALGOL 68

Influenced

Bourne Shell C shell Korn Shell (ksh) Bash Z Shell (zsh) Almquist Shell (ash) Dash

Running Today

Run examples using the official Docker image:

docker pull bash:5.2

Example usage:

docker run --rm -v $(pwd):/app -w /app bash:5.2 sh hello.sh
Last updated: