Est. 1979 Beginner

Bourne Shell

The original Unix shell written by Stephen Bourne at Bell Labs, which introduced structured scripting to Unix and became the ancestor of virtually every modern shell.

Created by Stephen R. Bourne

Paradigm Procedural, Scripting
Typing Dynamic, Weak
First Appeared 1979
Latest Version POSIX sh (IEEE Std 1003.1-2024)

The Bourne Shell (sh) is the foundational Unix shell, written by Stephen R. Bourne at Bell Laboratories and released as part of Unix Version 7 in 1979. It replaced the earlier Thompson shell with a dramatically more capable scripting language—introducing structured control flow, named variables, here-documents, signal trapping, and the ability to use scripts as pipeline filters. Every major shell in widespread use today—Bash, Korn shell, Zsh, Dash—descends directly from or was designed as a compatible replacement for the Bourne shell. The POSIX shell standard, which governs portable scripting across all Unix-like systems, is itself based on the System V Bourne shell. Few pieces of software have had a more lasting influence on the daily practice of computing.

History & Origins

The Thompson Shell Problem

The original Unix shell, written by Ken Thompson and shipping with Unix versions 1 through 6, was a minimal command interpreter. It lacked named variables (only positional parameters were available), had no built-in control flow (even if was an external command), and suffered a critical design flaw: a script file consumed standard input, making it impossible to use a shell script as a filter in a pipeline. For interactive use the Thompson shell was serviceable, but for scripting it was severely limited.

An intermediate step came with the PWB/UNIX shell (also called the Mashey shell), written by John Mashey at Bell Labs around 1977 for the Programmer’s Workbench variant of Unix. It added built-in control structures—if, while, and switch—which pointed the direction Bourne would take further.

Stephen Bourne at Bell Labs

Stephen R. Bourne studied mathematics at King’s College London and later at Trinity College, Cambridge, where he earned advanced degrees and worked on the ALGOL 68C compiler project, serving on the ALGOL 68 revision committee. He then joined Bell Laboratories and, as part of the Seventh Edition Unix team, wrote the shell that bears his name.

Development work on the Bourne shell began around 1976–1977. An internal version-controlled snapshot dated October 12, 1978 survives (version string sys137), showing the shell was in internal use at AT&T before its public release. In 1979, Unix Version 7 shipped the Bourne shell as /bin/sh, making it publicly available to the universities and research institutions that licensed AT&T Unix.

The ALGOL 68 Connection

Bourne’s academic background left a distinctive mark on the shell in two ways. First, the control structure syntax uses paired keywords derived from ALGOL: if closes with fi, case closes with esac, and loop bodies end with done—a pattern carried by every Bourne-compatible shell to this day.

Second, and more unusually, the shell’s C source code was written using C preprocessor macros that made C resemble ALGOL 68:

1
2
3
4
5
6
7
#define IF    if(
#define THEN  ){
#define ELSE  } else {
#define FI    }
#define WHILE while(
#define DO    ){
#define DONE  }

This gave the source code a distinctive appearance that later helped inspire the International Obfuscated C Code Contest. Russ Cox has documented this implementation technique in detail. It is a reminder that the Bourne shell was not just a tool but a creative work shaped by its author’s intellectual history.

Design Philosophy

The Bourne shell was designed to solve the scripting inadequacies of the Thompson shell while remaining consistent with Unix’s core philosophy of composable, text-based tools. Key design decisions include:

Structured programming as a first-class concern. Control structures (if, case, for, while, until) are built into the shell itself, not external programs. A Bourne shell script can express the same logical structures as a conventional program without relying on any external binaries.

Variables and environment propagation. Named variables with assignment and expansion, positional parameters, and the export built-in for passing variables to child processes solved the Thompson shell’s variable limitations.

Scripts as filters. The shell separates command input (the script file) from standard input (the data being processed), fixing the Thompson shell’s fatal constraint. A Bourne shell script can participate as a stage in a pipeline.

Signal handling. The trap built-in allows scripts to catch Unix signals and execute cleanup code, enabling reliable resource management in long-running scripts.

Composability. The shell orchestrates other programs; it does not try to replace them. The pipeline (|) and I/O redirection model, inherited from the Thompson shell, remains the fundamental mechanism.

Key Features

Variables and Parameter Expansion

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Variable assignment and expansion
name="World"
echo "Hello, ${name}!"          # Hello, World!

# Positional parameters (script arguments)
echo "Script: $0"               # script name
echo "First arg: $1"            # first argument

# Special variables
echo "PID: $$"                  # current process ID
echo "Exit status: $?"          # exit status of last command
echo "Arg count: $#"            # number of arguments
echo "All args: $@"             # all arguments as separate words

# Default value substitution
echo "${UNSET_VAR:-default}"    # prints 'default' if UNSET_VAR is unset

Control Structures

The Bourne shell introduced these structures as built-in syntax, not external commands:

 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/then/elif/else/fi
if [ "$status" -eq 0 ]; then
    echo "Success"
elif [ "$status" -eq 1 ]; then
    echo "Warning"
else
    echo "Error: $status"
fi

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

# for loop
for file in *.txt; do
    echo "Processing: $file"
done

# while loop
while [ "$count" -gt 0 ]; do
    echo "Count: $count"
    count=$(( count - 1 ))
done

I/O Redirection and Pipelines

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Output redirection
command > output.txt            # stdout to file (overwrite)
command >> output.txt           # stdout to file (append)
command 2> errors.txt           # stderr to file
command > all.txt 2>&1          # stdout to file, stderr to same file

# Pipeline
cat /etc/passwd | grep "root" | cut -d: -f1

# Here-document (multi-line input)
cat <<EOF
This is line one
This is line two
EOF

Command Substitution

1
2
3
4
5
6
7
# Backtick syntax (original Bourne shell form)
today=`date +%Y-%m-%d`
echo "Today is: $today"

# Used in expressions
lines=`wc -l < /etc/passwd`
echo "Password file has $lines lines"

Signal Trapping

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Trap signals for cleanup
tmpfile=`mktemp /tmp/script.XXXXXX`

cleanup() {
    rm -f "$tmpfile"
    echo "Cleaned up."
}

trap cleanup EXIT INT TERM

# Now the cleanup function runs even if the script is interrupted
echo "Working..." > "$tmpfile"

Shell Functions (Added in SVR2, 1984)

Shell functions were not part of the original 1979 Bourne shell but were added in System V Release 2:

1
2
3
4
5
6
7
# Function definition
greet() {
    echo "Hello, ${1:-World}!"
}

greet "Bourne"  # Hello, Bourne!
greet           # Hello, World!

Filename Globbing

1
2
3
ls *.c                # all C source files
ls file?.txt          # file followed by one character
ls file[123].txt      # file1.txt, file2.txt, or file3.txt

Feature Evolution Across Releases

The Bourne shell was never versioned in the way modern software is. Its capabilities evolved through the System V Unix release series:

ReleaseYearKey Additions
Unix V71979Variables, control structures (if, case, for, while, until), here-documents, command substitution (backticks), trap, eval, export, I/O redirection, pipelines, filename globbing
System III1981Comment character (#), set -- option delimiter
SVR11983shift n, ulimit, CDPATH
SVR21984Shell functions, type, unset, built-in echo and pwd, I/O redirection for built-ins
SVR31986getopts for portable option parsing, modern "$@" semantics, 8-bit clean text handling
SVR41989Job control, symbolic signal names for trap
SVR4.21992read -r flag for literal backslash handling
OpenSolaris2005AT&T source code released under CDDL license

Standardization

The Bourne shell’s greatest long-term impact may be that it became the basis for the POSIX shell standard. IEEE P1003.2, focused on shell language and utilities, worked through the mid-to-late 1980s and was ratified in September 1992 as IEEE Std 1003.2-1992. The standard was explicitly based on the System V Bourne shell as its primary reference implementation, with the committee reconciling behavior from V7, System V, BSD, and Korn shell variants.

In 2001, IEEE Std 1003.1-2001 (also known as the Single UNIX Specification version 3, or SUSv3) merged the shell standard into a unified POSIX specification. The current revision is IEEE Std 1003.1-2024.

A consequence of this history is that POSIX sh is not identical to the original 1979 Bourne shell. Several features programmers associate with the Bourne shell—shell functions, getopts—were added in the System V era, years after Unix V7. The original V7 shell is a historical artifact; POSIX sh is the living standard.

The ALGOL 68 Keyword Pairing Legacy

One of the most visible and lasting contributions of Bourne’s design is the keyword-pairing pattern for block termination. In ALGOL 68, structured constructs end with distinct closing markers. Bourne adapted this for shell syntax:

OpeningClosingConstruction
iffiReverse of if
caseesacReverse of case
dodoneCompletion form
then(implicit)Embedded in control flow

This pattern made shell scripts visually scannable—a closing fi is unambiguous, unlike a bare } that could close any block. Every POSIX-compatible shell in use today—Bash, Zsh, ksh, Dash—inherits these exact keywords.

Current Status

The original AT&T Bourne shell binary no longer ships as /bin/sh on mainstream modern operating systems:

  • Debian and Ubuntu Linux: /bin/sh points to Dash (Debian Almquist Shell), chosen for its strict POSIX compliance and fast startup
  • macOS: /bin/sh is Bash 3.2 (retained for compatibility; Apple uses GPLv2, not GPLv3)
  • Alpine Linux: /bin/sh is BusyBox ash, a compact POSIX sh implementation for container environments
  • FreeBSD: /bin/sh is the FreeBSD shell, derived from ash
  • Commercial Unix (HP-UX, AIX): These historically ran Bourne-derived shells; AIX uses ksh93 as its default

The original AT&T source code became publicly available in 2005 when Sun open-sourced it as part of OpenSolaris under the CDDL license. Two maintained forks preserve access to the original codebase: the Heirloom Bourne Shell (Gunnar Ritter’s portable port) and Schily bosh (originally developed by Jörg Schilling, who died in 2021; the fork includes POSIX extensions and is available in NetBSD pkgsrc and FreeBSD ports).

The shell’s true continuation is through the POSIX sh standard and its derivatives. Dash, the shell closest in spirit to the original—minimal, fast, strictly POSIX-compliant—is what most Linux systems use for /bin/sh today, directly serving the same role the Bourne shell served in 1979.

Bourne Shell vs. Bash: Key Differences

Many developers encounter Bourne shell compatibility constraints when writing portable scripts. The differences between original sh and Bash (bash) are significant:

FeatureBourne/POSIX shBash
Command substitution`cmd` (backtick)$(cmd) preferred
Arithmeticexpr (external command)$(( )) built-in
ArraysNoneIndexed and associative
[[ ]] conditionalsNot availableAvailable (extended test)
=~ regex matchingNot availableAvailable (Bash 3.0+)
String manipulationLimited ${var#pat} formsRich expansion operators
local in functionsNot in original V7Available
Here strings (<<<)Not availableAvailable
source built-inNot in original (. only)Both . and source

For maximum portability, scripts intended to run on any POSIX system should use only POSIX sh features, use the #!/bin/sh shebang, and be validated with a tool such as ShellCheck in POSIX mode.

Why the Bourne Shell Matters

The Bourne shell’s influence on computing is difficult to overstate. It defined the scripting model that Unix system administrators have used for nearly five decades. It introduced the structured control flow that made shell scripts viable programs rather than just command shortcuts. And by becoming the basis for the POSIX standard, it ensured that a script written to its conventions would be portable across every Unix-like system ever built.

The shell’s family tree traces the history of free and open-source Unix. Bash, GNU’s free Bourne replacement, became the default shell of Linux, which became the default operating system of the internet. Ash and Dash, lightweight reimplementations for resource-constrained environments, power the init systems of billions of embedded devices and containers. Every time a CI/CD pipeline runs a shell script, every time a Docker container starts with /bin/sh, every time a configure script runs before compilation—the Bourne shell’s design decisions from 1979 are at work.

For developers, the Bourne shell is not merely a historical curiosity. Understanding it illuminates why Bash behaves as it does, why some shell constructs are portable and others are not, and why decades of Unix infrastructure is written the way it is. The if/fi pairing, the $? exit status, the pipeline-first philosophy—these are Bourne’s contributions, and they remain as relevant in 2026 as they were when Unix Version 7 shipped.

Timeline

1979
Bourne Shell first released publicly as part of Unix Version 7 (Seventh Edition Unix) at AT&T Bell Labs, replacing the earlier Thompson shell as the default /bin/sh
1981
Unix System III ships the Bourne shell with early enhancements, including the comment character (#) and the set -- option delimiter
1983
System V Release 1 (SVR1) adds shift n, ulimit, and CDPATH built-ins to the shell
1984
System V Release 2 (SVR2) introduces shell functions (name() { ... }), the type built-in, unset, and built-in echo and pwd commands
1986
System V Release 3 (SVR3) adds the getopts built-in for portable option parsing, modern "$@" semantics, and 8-bit clean operation
1989
System V Release 4 (SVR4) adds job control and symbolic signal names for the trap built-in; the same year, Brian Fox releases Bash as the GNU Project's free Bourne-compatible replacement
1992
IEEE Std 1003.2-1992 (POSIX shell standard) is ratified, codifying the System V Bourne shell as the basis for portable shell scripting; SVR4.2 also adds read -r
2001
IEEE Std 1003.1-2001 (SUSv3) merges the shell standard into the unified POSIX specification, broadening its reach across all Unix-like platforms
2005
AT&T open-sources the Bourne shell source code as part of OpenSolaris under the CDDL license, making the original implementation publicly available for the first time
2006
Gunnar Ritter releases the Heirloom Bourne Shell, a portable source port derived from the OpenSolaris code, preserving the original AT&T implementation on modern systems
2024
IEEE Std 1003.1-2024 (the most recent POSIX revision) updates the shell standard with security and internationalization enhancements; Schily bosh, originally developed by Jörg Schilling (d. 2021), remains available as a fork of the original source

Notable Uses & Legacy

AT&T Unix System V

The Bourne shell was the default /bin/sh on all AT&T System V Unix releases throughout the 1980s and 1990s, serving as the primary administrative scripting environment for commercial Unix deployments worldwide.

GNU Autoconf configure Scripts

Autoconf generates ./configure scripts that target strict Bourne/POSIX sh compatibility as the lowest common denominator, meaning every open-source package using Autoconf ships scripts that must run on a Bourne-compatible shell. The Autoconf manual documents Bourne shell portability requirements explicitly.

Unix System Init Scripts

Every commercial Unix system (HP-UX, AIX, Solaris, IRIX) used Bourne shell scripts as rc (run-control) init scripts for boot-time system initialization. This pattern carried forward into early Linux SysV init and BSD /etc/rc.d/ scripts.

Embedded and Container Systems

BusyBox ash, a Bourne-compatible shell, provides /bin/sh in Alpine Linux, which serves as the base image for a large fraction of Docker containers. Bourne shell syntax therefore remains the scripting standard in container-based infrastructure.

POSIX sh Scripting

Scripts written to the POSIX sh standard—derived directly from the System V Bourne shell—are used across all Unix-like operating systems for portable automation. Continuous integration platforms, cloud-init scripts, and package install scripts commonly target POSIX sh for maximum portability.

Language Influence

Influenced By

Thompson Shell PWB Shell ALGOL 68

Influenced

Bash Korn Shell (ksh) Z Shell (zsh) Dash Almquist Shell (ash)

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: