Bash#

Bash is the operator’s default keyboard on Linux. Most-deployed shell in the world, default login shell on nearly every distribution, the shell almost every piece of documentation, every runbook, and every exploit writeup assumes. The operator who reads and writes Bash at speed moves through the platform without friction.

Bash as an interactive shell, line editing, history, completion, job control, prompts, the bits that turn the terminal into a usable cockpit. Bash as a scripting language, syntax, control flow, parameter expansion, I/O, error handling, and the patterns that keep scripts safe under load. The chapters below cover both faces.

Setup#

Bash ships with most Linux distributions and macOS. Install or upgrade with a package manager:

$ sudo apt install bash

$ brew install bash

Files#

The init files Bash sources at startup. Which files run depends on whether the shell is a login shell, an interactive non-login shell, or a non-interactive shell, the trip-up here is that login shells skip ~/.bashrc unless you source it explicitly:

  • /etc/profile , system-wide login shell init.

  • ~/.bash_profile , per-user login shell init.

  • ~/.bashrc , per-user interactive non-login init.

  • ~/.bash_logout , runs at login shell exit.

  • ~/.inputrc , readline (line editor) configuration.

The login / non-login distinction trips up beginners. Common pattern:

$ [[ -f ~/.bashrc ]] && source ~/.bashrc

Line Editing (readline)#

Bash uses GNU readline for input editing, the same library MySQL, Python’s REPL, and many other CLIs use. Defaults are Emacs-style; set -o vi switches to vi-style for the current shell, or put set editing-mode vi in ~/.inputrc to make it stick.

Most-used keys (Emacs mode):

Keys

Action

Ctrl-A / Ctrl-E start / end of line

Ctrl-F / Ctrl-B forward / back one char

Alt-F / Alt-B

forward / back one word

Ctrl-K

kill to end of line

Ctrl-U

kill to start

Ctrl-W

kill word back

Alt-D

kill word forward

Ctrl-Y

yank (paste)

Ctrl-R

reverse history search

Ctrl-G

abort search

Ctrl-L

clear screen

Ctrl-_

undo

Tab

completion

Alt-.

last argument of previous command

History#

Bash records every interactive command into ~/.bash_history and keeps a per-session list in memory. The history-expansion shortcuts (!!, !$, !*, !str) are the fastest way to recall a recent command without arrowing through it.

$ history
$ history | grep ssh

$ !!
$ !$
$ !*
$ !abc
$ ^old^new

History config in ~/.bashrc:

$ HISTSIZE=10000
$ HISTFILESIZE=20000
$ HISTCONTROL=ignoredups:erasedups
$ shopt -s histappend
$ PROMPT_COMMAND='history -a'

Completion#

Tab-completion is everywhere in Bash. Programmable completion, via the bash-completion package, often installed by default, adds context-aware suggestions for hundreds of CLI tools (apt subcommands, git refs, kubectl resources). Each tool registers its own completer through complete.

$ sudo apt install bash-completion

$ if [ -f /etc/bash_completion ]; then
  $ source /etc/bash_completion
$ fi

Globbing#

Glob patterns expand on the command line before the program sees them. shopt controls extended behaviors: globstar enables ** for recursive matching, nullglob makes a non-matching pattern expand to nothing instead of itself, and nocaseglob makes matching case-insensitive.

$ shopt -s globstar
$ shopt -s nullglob
$ shopt -s nocaseglob

$ ls **/*.go

Aliases and Functions#

Aliases are simple text substitutions for a single command; functions are full mini-programs with arguments, local variables, and control flow. Reach for an alias when you want a shorter spelling of a command, and a function when you need to compose multiple steps or take parameters.

$ alias ll='ls -lAh'
$ alias gs='git status'
$ alias ..='cd ..'

$ mkcd() { mkdir -p "$1" && cd "$1"; }
$ port() { lsof -nP -iTCP:"$1" -sTCP:LISTEN; }

Prompt#

Default prompts are spartan. The classic PS1 is a string with backslash escapes for the parts you usually want, \u user, \h host, \w working dir, \$ privilege marker, and ANSI escape sequences for color, wrapped in \[\] so readline measures them correctly:

$ PS1='\u@\h:\w\$ '
$ PS1='\[\e[36m\]\u@\h\[\e[0m\] \w \$ '

Most operators in 2026 use a prompt manager, see Customization.

Strengths#

What Bash does better than any modern alternative, mostly things that come from being everywhere for thirty-five years. The list below is short on purpose: ubiquity and stability beat features when the shell is going to land on a host you didn’t choose.

  • Ubiquitous, assume Bash is installed; most #!/bin/sh scripts also run on Bash.

  • POSIX-compatible core, portable.

  • Mature, decades of documentation, examples, and Q&A.

  • Default on most servers and CI environments.

POSIX Mode#

Bash defaults to its full feature set. POSIX mode restricts it to POSIX-shell behavior, useful when you want to lint a script against the standard while still running on the same binary.

set -o posix              # turn on inside a running script
set +o posix              # turn off

$ bash --posix -c 'echo hi'   # whole shell in POSIX mode
$ bash --posix script.sh

In POSIX mode Bash drops Bashisms like [[, ${var,,}, <(cmd), and function name(), and several builtin defaults change (set -e semantics, echo without flags, getopts quirks). For genuine portability testing, run the script under dash or busybox sh, Bash’s POSIX mode still tolerates some looseness that real POSIX shells reject.

If a script targets POSIX, write it for /bin/sh directly (see Sh for the Bashism crosswalk and checkbashisms); if a script targets Bash, use Bashisms freely with #!/usr/bin/env bash.

Weaknesses#

Where Bash shows its age. None of these are dealbreakers for everyday work, most operators learn to live with them, but they explain why so many of the modern alternatives in this section exist.

  • Slow startup with heavy .bashrc files.

  • Default completion is plain without bash-completion installed.

  • Older Bash on macOS, system Bash is 3.x for licensing reasons; install via Homebrew if you need 5.x features.

  • Quirks, argument parsing, quoting, IFS behavior; harder to learn than Fish or Zsh.

When to Stay with Bash#

The default answer for any shared, scripted, or remote environment. Bash is what is on the box; matching the shell on the host eliminates a category of “works on my machine” surprises before they happen.

  • Operational work where the shell on the box is bash and you don’t want surprises.

  • Scripts that need to run on most servers without further install, bash is the lowest common denominator that’s pleasant to write in. (For strict POSIX portability target sh, see Sh.)

  • Servers, containers, CI runners.

  • Wherever someone else’s environment is what you’re working in.

For your own desktop, almost any of the others is more pleasant.

Chapters#

Overview

Bash as a command interpreter. Syntax, operators, control flow, functions, errors, runtime. Variable types live under Structures.

Overview
I/O and Pipelines

Streams, redirection, heredocs, pipes, process substitution, named pipes. The wiring layer between programs.

I/O and Pipelines
Structures

Operational patterns over scalars, arrays, and associative arrays. When to reach for each.

Structures
Algorithms

Basics for shell utilities. When Bash is enough; when it is not.

Algorithms
Libraries

Re-use through sourcing, external programs, shared helper directories.

Libraries
Frameworks

Projects that add structure to argument parsing, testing, and packaging.

Frameworks
Networking

coreutils and the well-known network utilities. Bash orchestrates; it does not speak the network itself.

Networking
Patterns

Strict mode, traps, idioms that keep scripts safer and more maintainable.

Patterns
Tools

The toolchain. POSIX utilities and a few third-party programs.

Tools