Est. 1989 Beginner

Bash

The GNU Project's shell and command language that became the default Unix/Linux shell, powering system administration and automation worldwide.

Created by Brian Fox

Paradigm Procedural, Scripting
Typing Dynamic, Weak
First Appeared 1989
Latest Version Bash 5.2 (2022)

Bash (Bourne Again SHell) is both a command-line interpreter and a scripting language that has become the lingua franca of Unix and Linux system administration. Born in 1989 as the GNU Project’s free replacement for the original Bourne shell, Bash combined the best features of its contemporaries—the Bourne shell, the C shell, and the Korn shell—into a single, powerful, and freely distributable package. Today it ships as the default shell on most Linux distributions and remains available on macOS, making it one of the most widely deployed programming environments in the world.

History & Origins

The Bourne Shell Problem

To understand Bash, you must first understand what it replaced. The Bourne shell (sh), written by Stephen Bourne at Bell Labs and released with Unix Version 7 in 1979, was the foundational Unix shell. It was well-designed but proprietary—part of AT&T’s Unix, not freely distributable. In the early 1980s, the GNU Project, founded by Richard Stallman, aimed to create a completely free Unix-compatible operating system. A free shell was essential to that mission.

Brian Fox and the GNU Shell

In January 1988, Richard Stallman commissioned Brian Fox to write a free shell compatible with the Bourne shell but including features from the Korn shell (ksh) and C shell (csh). Fox began development and released the first public version of Bash—version 0.99—on June 8, 1989. The name “Bourne Again SHell” was a pun combining the name of the shell it replaced with the idea of being “born again.”

Bash incorporated:

  • Bourne shell compatibility: Scripts written for sh would largely run unchanged
  • C shell conveniences: Command history, ! history expansion, aliases
  • Korn shell features: Command-line editing, job control, functions, arrays

Chet Ramey Takes the Reins

Brian Fox’s involvement with Bash was relatively brief. Chet Ramey, then a student at Case Western Reserve University, had been contributing bug fixes since the early days. In 1990, Ramey became the primary maintainer of Bash—a role he has held continuously for over three decades. This extraordinary continuity of stewardship has been a key factor in Bash’s stability and consistent development.

Design Philosophy

Bash follows the Unix philosophy of doing one thing well, but its “one thing” is broad: providing an interactive interface to the operating system and a scripting language powerful enough to automate complex workflows. Several principles guide its design:

  • POSIX Compliance: Bash conforms to the POSIX.1 standard for shell behavior, ensuring portability across Unix-like systems
  • Backward Compatibility: Scripts written for older versions of Bash should continue to work; breaking changes are avoided
  • Interactive Usability: Features like readline integration, command completion, and history make interactive use comfortable
  • Incremental Complexity: Simple tasks are simple; complex tasks are possible

Key Features

Variables and Parameter Expansion

Bash variables are untyped by default—everything is a string. Parameter expansion provides powerful string manipulation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
name="World"
echo "Hello, ${name}!"          # Hello, World!
echo "${name,,}"                 # world (lowercase)
echo "${name^^}"                 # WORLD (uppercase)
echo "${name:0:3}"               # Wor (substring)
echo "${name/World/Bash}"        # Hello, Bash!

# Default values
echo "${UNSET_VAR:-default}"    # default
echo "${UNSET_VAR:=assigned}"   # assigned (also assigns the variable)

Arrays

Bash supports both indexed and associative (hash) arrays:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Indexed array
fruits=("apple" "banana" "cherry")
echo "${fruits[0]}"              # apple
echo "${fruits[@]}"              # apple banana cherry
echo "${#fruits[@]}"             # 3 (length)

# Associative array (Bash 4.0+)
declare -A capitals
capitals["France"]="Paris"
capitals["Japan"]="Tokyo"
echo "${capitals["France"]}"    # Paris
echo "${!capitals[@]}"          # France Japan (keys)

Control Flow

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Conditionals
if [[ -f "$file" ]]; then
    echo "File exists"
elif [[ -d "$file" ]]; then
    echo "It's a directory"
else
    echo "Not found"
fi

# C-style for loop
for ((i = 0; i < 5; i++)); do
    echo "Iteration $i"
done

# Iterating over values
for fruit in "${fruits[@]}"; do
    echo "Fruit: $fruit"
done

# While loop
while IFS= read -r line; do
    echo "Line: $line"
done < input.txt

Functions

1
2
3
4
5
6
7
8
greet() {
    local name="${1:-World}"    # local variable with default
    echo "Hello, ${name}!"
    return 0
}

greet "Bash"    # Hello, Bash!
greet           # Hello, World!

Pipeline and Redirection

The pipeline is Bash’s most powerful feature—connecting programs so output of one becomes input of the next:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Classic Unix pipeline
cat /var/log/syslog | grep "ERROR" | sort | uniq -c | sort -rn | head -10

# Redirection
command > output.txt        # stdout to file (overwrite)
command >> output.txt       # stdout to file (append)
command 2> errors.txt       # stderr to file
command 2>&1                # stderr to stdout
command &> all.txt          # both to file (Bash 4+)

# Process substitution
diff <(sort file1.txt) <(sort file2.txt)

Here Documents and Here Strings

1
2
3
4
5
6
7
8
# Here document
cat <<EOF
This is a multi-line
string that ends at EOF
EOF

# Here string
grep "pattern" <<< "search in this string"

Arithmetic

1
2
3
4
5
6
7
8
# Arithmetic expansion
result=$(( 2 + 3 * 4 ))     # 14
echo $(( 10 / 3 ))          # 3 (integer division)
echo $(( 10 % 3 ))          # 1 (modulo)

# let and (( ))
let "x = 5 + 3"
(( x++ ))

Pattern Matching and Globbing

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Filename globbing
ls *.txt                     # all .txt files
ls file?.txt                 # file followed by one character
ls file[123].txt             # file1.txt, file2.txt, or file3.txt

# Extended globbing (extglob)
shopt -s extglob
ls !(*.txt)                  # everything except .txt files
ls @(*.txt|*.md)             # .txt or .md files

# Regex matching (Bash 3.0+)
if [[ "$string" =~ ^[0-9]+$ ]]; then
    echo "It's a number"
fi

Job Control

Bash provides job control for managing multiple processes:

1
2
3
4
5
long_running_command &       # run in background
jobs                         # list background jobs
fg %1                        # bring job 1 to foreground
bg %2                        # resume job 2 in background
wait                         # wait for all background jobs

Bash vs. Other Shells

FeatureBashZshFishPOSIX sh
First Released1989199020051992 (standardized)
Default on LinuxYes (most distros)VariesNoUsed in scripts
Default on macOS≤10.1410.15+NoAvailable
Associative ArraysBash 4.0+YesYes (dicts)No
Regex Matching=~=~Built-inNo
True/False0=success0=successBetter0=success
Config File.bashrc/.bash_profile.zshrcconfig.fish.profile
Script CompatibilityPOSIX mostlyPOSIX mostlyNot POSIXPOSIX strict

The Shellshock Vulnerability

In September 2014, security researcher Stéphane Chazelas discovered CVE-2014-6271, known as “Shellshock”—one of the most severe vulnerabilities in Bash’s history. The flaw involved how Bash processed function definitions passed through environment variables. An attacker could append arbitrary commands after a function definition, and Bash would execute them when invoked:

1
2
# Simplified illustration of the vulnerability
env x='() { :;}; echo vulnerable' bash -c "echo test"

Because many web servers (via CGI) and network services invoked Bash with user-controlled environment variables, this created a remote code execution path. Patches were released within days, but the incident highlighted the risks of Bash being invoked by network-facing services. The vulnerability affected Bash versions from at least 1.14 through 4.3.

Scripting Best Practices

Experienced Bash programmers follow conventions that make scripts more robust:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env bash
set -euo pipefail    # Exit on error, unbound variables, pipe failures

# Use [[ ]] instead of [ ] for conditionals
if [[ -n "$var" ]]; then ...

# Quote variables to prevent word splitting
process_file "$filename"

# Use local variables in functions
my_function() {
    local result
    result=$(compute_something)
    echo "$result"
}

# Trap for cleanup
cleanup() {
    rm -f "$tmpfile"
}
trap cleanup EXIT

ShellCheck: The Linter

ShellCheck is an open-source static analysis tool for shell scripts that catches common mistakes, POSIX incompatibilities, and style issues. It has become an essential part of modern Bash development, often integrated into CI/CD pipelines:

1
shellcheck my_script.sh

Evolution

Bash 4.0 (2009): Associative Arrays and Coproc

The addition of associative arrays in Bash 4.0 was transformative for scripting complex data:

1
2
3
declare -A person
person[name]="Alice"
person[age]="30"

Coprocesses allowed two-way communication with a background process:

1
2
3
coproc myproc { python3 -u interactive_script.py; }
echo "input" >&"${myproc[1]}"
read response <&"${myproc[0]}"

Bash 5.0 (2019): New Variables and Improvements

Bash 5.0 introduced EPOCHSECONDS (seconds since the Unix epoch) and EPOCHREALTIME (with microsecond precision) as convenient built-in variables, eliminating the need to call external utilities for timestamp operations in time-sensitive scripts.

Bash 5.2 (2022)

Bash 5.2 refined behavior around string transformations, fixed edge cases in word splitting and glob expansion, and improved the behavior of local variables in recursive function calls.

Current Relevance

Bash’s relevance in 2026 is undeniable: it is the scripting language of Linux infrastructure. While Python has become preferred for complex automation, Bash remains the first choice for:

  • System startup and init scripts: systemd unit files frequently call Bash scripts
  • Container orchestration: Kubernetes liveness/readiness probes, Docker entrypoints
  • CI/CD pipelines: Every major platform (GitHub Actions, GitLab CI, CircleCI) runs Bash by default
  • Cloud provisioning: AWS user data scripts, Azure custom script extensions, GCP startup scripts
  • Developer tooling: ./configure scripts, Makefile recipes, install scripts

The shell scripting ecosystem around Bash—ShellCheck for linting, bats (Bash Automated Testing System) for testing, and shellspec for BDD-style tests—reflects a mature, professional toolchain.

Why Bash Matters

Bash occupies a unique position in computing history: it is both old infrastructure and modern necessity. Created as part of the GNU Project’s mission to create free software, it became the default shell of the Linux operating system—which became the default operating system of the internet. Every major cloud provider, every container runtime, every CI/CD system depends on Bash.

Its influence extends beyond direct use. Fish and Dash define themselves partly in relation to Bash—either improving on it or optimizing for specific use cases. PowerShell, Microsoft’s modern shell for Windows, reportedly drew inspiration from the Unix shell tradition’s pipeline and scripting concepts, though it was designed independently with a distinct object-oriented model.

For any developer working with Unix-like systems, Bash is not optional knowledge—it is foundational. Understanding Bash means understanding how Unix systems are administered, how software is built, and how infrastructure is automated at scale.

Timeline

1989
Bash first released by Brian Fox for the GNU Project as a free replacement for the Bourne shell; version 0.99 was the initial public release on June 8, 1989
1990
Chet Ramey takes over maintenance of Bash after Brian Fox's involvement with the GNU Project shifted; Ramey has maintained it ever since
1994
Bash 1.14 released with improved POSIX compliance and feature refinements
1996
Bash 2.0 released, adding improved array support, new expansion syntax, and enhanced POSIX compliance
1998
Bash becomes the default shell on most major Linux distributions, cementing its role as the de facto Unix shell
2004
Bash 3.0 released with improved regex support via the =~ operator and additional POSIX conformance
2009
Bash 4.0 released with associative arrays, coprocess support, and the mapfile built-in command
2014
Shellshock vulnerability (CVE-2014-6271) discovered in Bash, allowing arbitrary code execution via specially crafted environment variables; widely patched within days
2016
Bash 4.4 released, adding new string transformation operations and improved Unicode handling
2019
Bash 5.0 released with new variables (EPOCHSECONDS, EPOCHREALTIME), improved associative array support, and expanded globbing options
2022
Bash 5.2 released, adding new string transformation features and fixes to long-standing edge cases

Notable Uses & Legacy

Linux System Administration

Bash is the default login shell on most Linux distributions and is universally used for system startup scripts (init/systemd), cron jobs, and administrative automation.

CI/CD Pipelines

GitHub Actions, GitLab CI, Jenkins, and virtually every CI/CD platform use Bash as the default scripting language for build, test, and deployment steps.

macOS

Bash was the default interactive shell on macOS from its early releases through 10.14 (Mojave, 2018); Apple switched the default to Zsh in macOS 10.15 (Catalina, 2019), though Bash 3.2 remains available for compatibility.

Docker and Container Tooling

Docker's official entrypoint scripts, Kubernetes health check probes, and the vast majority of container initialization scripts are written in Bash.

Infrastructure Automation

Ansible uses Bash as the default execution shell on managed nodes; Terraform provisioners and cloud-init scripts commonly use Bash for instance bootstrapping.

Open Source Build Systems

Autoconf-generated configure scripts, GNU Makefile rules, and the build systems for countless open source projects including the Linux kernel's kbuild system use Bash scripts extensively.

Language Influence

Influenced By

Bourne Shell (sh) C Shell (csh) Korn Shell (ksh) POSIX sh

Influenced

Fish 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 bash hello.sh
Last updated: