Bash, short for the Bourne-Again SHell, is the command interpreter and scripting language of the GNU Project and the default interactive shell on most Linux distributions. It reads commands typed in a terminal or stored in a script, expands variables, wildcards and substitutions, starts programs as child processes and reports their exit status.
Brian Fox wrote Bash in 1989 as a free replacement for the Bourne shell (sh), and Chet Ramey has maintained it for most of its history. It is largely POSIX-compatible while adding arrays, arithmetic, command history and line editing, which makes it both the everyday working environment of Linux administrators and the most common language for automation glue.
How Bash processes a command line
Bash turns each input line into commands through a fixed sequence of stages: it splits the text into tokens, identifies operators such as |, && and ;, applies redirections, performs expansions in a defined order, removes quotes and finally executes the result. Understanding this order explains most surprising behavior in scripts.
The following table lists the expansions in the order Bash applies them. Brace expansion happens first and is purely textual, while word splitting and pathname expansion act only on unquoted results, which is why quoting matters so much.
| Order | Expansion | Example | Result |
|---|---|---|---|
| 1 | Brace expansion | file{1..3} | file1 file2 file3 |
| 2 | Tilde expansion | ~/notes | Path inside the home directory |
| 3 | Parameter and variable expansion | ${USER} | Value of the variable |
| 3 | Arithmetic expansion | $((7 * 6)) | 42 |
| 3 | Command substitution | $(date +%F) | Output of the command |
| 4 | Word splitting | Unquoted $list | Separate words split on IFS |
| 5 | Pathname expansion | *.log | Matching file names |
| 6 | Quote removal | "a b" | One word: a b |
Parameter, arithmetic and command substitution share one step and run left to right. After expansion, Bash looks the command name up as a function, builtin or external program found through PATH, then forks a child process for external programs and waits for its exit status, available in $?.
Interactive use and startup files
Bash behaves differently depending on how it was started, and the startup files it reads follow from that mode. Confusing these files is the most common reason an alias or environment variable works in one terminal but not in another.
| Invocation mode | Typical trigger | Files read |
|---|---|---|
| Interactive login shell | SSH session, console login | /etc/profile, then the first of ~/.bash_profile, ~/.bash_login, ~/.profile |
| Interactive non-login shell | New terminal tab in a desktop | ~/.bashrc (often also a system-wide bashrc) |
| Non-interactive shell | Running a script | File named in BASH_ENV, if set |
| Invoked as sh | Script with #!/bin/sh on systems linking sh to Bash | POSIX mode with /etc/profile and ~/.profile for logins |
A widespread convention is to keep environment variables in `~/.bash_profile` or ~/.profile and source ~/.bashrc from it, while aliases and prompt settings live in `~/.bashrc`. Interactive features include Readline line editing, history search with Ctrl+R, tab completion and job control with fg, bg and jobs.
Bash as a scripting language
A Bash script is a text file of commands that starts with a shebang line such as #!/usr/bin/env bash and is made executable with chmod. Bash scripting is strongest when it coordinates other programs: moving files, chaining tools through pipes, checking exit codes and reacting to failures.
Bash provides variables, indexed and associative arrays (the latter since version 4.0), functions with local variables, conditionals with [[ ... ]], loops, case statements, here-documents and process substitution such as <(command). Each command returns an exit status from 0 to 255, where 0 means success.
| Status | Meaning | Typical cause |
|---|---|---|
| 0 | Success | Command completed normally |
| 1 | General failure | Error reported by the program |
| 2 | Misuse of a builtin | Syntax error or wrong option |
| 126 | Found but not executable | Missing execute permission |
| 127 | Command not found | Typo or program not in PATH |
| 128 + n | Killed by signal n | 130 after Ctrl+C (SIGINT, signal 2) |
Many teams start scripts with `set -euo pipefail`: -e stops on an unhandled error, -u treats unset variables as errors and pipefail makes a pipeline fail when any stage fails. These options catch real bugs, but -e has well-known exceptions inside conditionals and command substitutions, so explicit error checks remain necessary in critical code.
Bash compared with other shells
Bash sits between the minimal POSIX shell and feature-rich interactive shells. The choice depends on whether the priority is portability, interactive comfort or startup speed.
| Shell | Strength | Limitation | Typical role |
|---|---|---|---|
| Bash | Ubiquitous, rich scripting features | Slower startup than dash | Default login shell on most distributions |
| dash | Small and fast, strict POSIX | No arrays or Bash extensions | /bin/sh on Debian and Ubuntu |
| Zsh | Advanced completion and theming | Not installed everywhere | Default interactive shell on macOS since 2019 |
| fish | Friendly defaults and suggestions | Not POSIX-compatible | Interactive use only |
A script that uses arrays or [[ ]] is a Bash script, not a POSIX sh script, and fails under dash. The shebang must therefore name bash whenever Bash features are used. Apple still ships Bash 3.2 with macOS because later versions moved to GPLv3, so portable scripts avoid features newer than that release.
Pitfalls and security considerations
Most Bash bugs come from unquoted expansions and from treating text as structured data. Experienced administrators follow a few defensive rules.
- Quote every variable expansion as
"$var"so that spaces and wildcards in file names do not split or expand. - Never parse the output of ls; iterate over a glob such as
for f in *.txtinstead. - Avoid eval on untrusted input, because it executes arbitrary text as code.
- Check scripts with a linter such as ShellCheck, which catches quoting and portability errors automatically.
- Switch to a general-purpose language when a script needs complex data structures, floating-point math or more than a few hundred lines.
Bash itself has had serious vulnerabilities. Shellshock (CVE-2014-6271), disclosed in September 2014, let attackers execute commands through crafted environment variables passed to CGI scripts and other services. It remains a reminder to keep the shell patched and to avoid exposing it to untrusted input.
Understanding it simply
Bash is a translator between a person and the operating system. A user types a short instruction, such as copy these files or show running programs. Bash works out exactly what is meant and asks the system to carry it out.
The same instructions can be saved in a file and replayed at any time. That is a script. Bash turns repetitive manual work into a repeatable recipe, which is why it is found on almost every Linux machine.
Applied in practice
A system administrator at a mid-sized company must archive web server logs every night. She writes a 40-line Bash script that compresses logs older than one day with gzip, copies them to a backup server with rsync, deletes local archives older than 30 days and writes a one-line summary to the system journal.
The script starts with set -euo pipefail, quotes every path and checks the exit status of rsync explicitly. A systemd timer runs it at 02:30 each night. When the backup server was unreachable one night, rsync returned a non-zero status, the script stopped before deleting anything and an alert was raised. No log data was lost, and the same script now runs unchanged on twelve servers.