Bash

On this page 7

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.

OrderExpansionExampleResult
1Brace expansionfile{1..3}file1 file2 file3
2Tilde expansion~/notesPath inside the home directory
3Parameter and variable expansion${USER}Value of the variable
3Arithmetic expansion$((7 * 6))42
3Command substitution$(date +%F)Output of the command
4Word splittingUnquoted $listSeparate words split on IFS
5Pathname expansion*.logMatching file names
6Quote removal"a b"One word: a b
Bash expansions in processing order

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 modeTypical triggerFiles read
Interactive login shellSSH session, console login/etc/profile, then the first of ~/.bash_profile, ~/.bash_login, ~/.profile
Interactive non-login shellNew terminal tab in a desktop~/.bashrc (often also a system-wide bashrc)
Non-interactive shellRunning a scriptFile named in BASH_ENV, if set
Invoked as shScript with #!/bin/sh on systems linking sh to BashPOSIX mode with /etc/profile and ~/.profile for logins
Which startup files Bash reads

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.

StatusMeaningTypical cause
0SuccessCommand completed normally
1General failureError reported by the program
2Misuse of a builtinSyntax error or wrong option
126Found but not executableMissing execute permission
127Command not foundTypo or program not in PATH
128 + nKilled by signal n130 after Ctrl+C (SIGINT, signal 2)
Common exit status values

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.

ShellStrengthLimitationTypical role
BashUbiquitous, rich scripting featuresSlower startup than dashDefault login shell on most distributions
dashSmall and fast, strict POSIXNo arrays or Bash extensions/bin/sh on Debian and Ubuntu
ZshAdvanced completion and themingNot installed everywhereDefault interactive shell on macOS since 2019
fishFriendly defaults and suggestionsNot POSIX-compatibleInteractive use only
Bash and related shells

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 *.txt instead.
  • 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.