Advanced Bash — Trivia & Interesting Facts¶
Surprising, historical, and little-known facts about Bash and advanced shell scripting.
Bash is named after a pun¶
Bash stands for "Bourne Again SHell," a play on the Bourne shell (sh) written by Stephen Bourne at Bell Labs in 1979. Brian Fox wrote Bash in 1989 for the GNU Project. The name is simultaneously a religious pun ("born again") and a nod to the shell it replaced.
The shebang line predates Bash by a decade¶
The #! ("shebang" or "hashbang") mechanism was introduced by Dennis Ritchie in January 1980 for Version 8 Unix. The name "shebang" likely derives from "sharp" (#) + "bang" (!). Some claim it comes from the slang word "shebang" meaning "the whole thing," as in "the whole shebang."
Bash arrays were not zero-indexed by accident¶
Bash arrays are zero-indexed, but associative arrays (added in Bash 4.0, 2009) can use arbitrary string keys. Zsh arrays, by contrast, are one-indexed by default — a difference that has caused countless porting bugs between the two shells and sparked decades of flamewars.
Process substitution was revolutionary¶
The <(command) syntax (process substitution) creates a temporary named pipe under /dev/fd/ and passes its path as a filename argument. This was first implemented in Bash 1.0 and remains one of the most powerful yet underused features. It allows you to diff two command outputs directly: diff <(ls dir1) <(ls dir2).
POSIX compliance is intentionally incomplete¶
Bash deliberately extends POSIX sh syntax with features like [[ ]], (( )), arrays, and brace expansion. When invoked as sh, Bash enters a POSIX-compatibility mode that disables many of these extensions. This dual personality has been both Bash's greatest strength and the source of its most confusing portability bugs.
The "set -e" trap is more subtle than anyone thinks¶
set -e (errexit) is supposed to abort the script on any command failure. But it is silently disabled inside if conditions, while conditions, commands joined with && or ||, and subshells in certain contexts. The exact rules span over 500 words in the Bash manual and have been called "the most misunderstood feature in shell scripting."
Bash has a built-in network redirector¶
Since Bash 2.04 (2000), you can open TCP and UDP connections using /dev/tcp/host/port and /dev/udp/host/port. These are not real device files — they are handled internally by Bash. Many distributions compile Bash without this feature for security reasons, but when available, echo "GET /" > /dev/tcp/example.com/80 sends an HTTP request with zero external tools.
Here-strings were borrowed from zsh¶
The <<< (here-string) syntax that lets you write grep pattern <<< "$variable" was originally a zsh feature. Bash adopted it in version 2.05b (2002). It avoids the subshell penalty of echo "$variable" | grep pattern because no pipe is created.
Coproc is Bash's least-known feature¶
Bash 4.0 (2009) added the coproc keyword for creating coprocesses — background processes with bidirectional pipes. Despite being over 15 years old, it remains so obscure that many experienced Bash programmers have never used it. It creates file descriptors in the COPROC array for reading and writing to the subprocess.
The $RANDOM variable uses a linear congruential generator¶
Bash's built-in $RANDOM produces values from 0 to 32767 using a simple LCG seeded with the PID and current time. The sequence is deterministic given the seed, making it unsuitable for cryptographic purposes. For better randomness, Bash scripts should read from /dev/urandom.
Brace expansion can generate sequences with leading zeros¶
Since Bash 4.0, {01..10} generates 01 02 03 04 05 06 07 08 09 10 with zero-padding. This feature was borrowed from zsh and saves the common seq -w or printf "%02d" pattern. Most Bash users discover it years into their shell careers.
The DEBUG trap can implement a Bash debugger¶
The trap 'commands' DEBUG mechanism fires before every simple command. Combined with BASH_COMMAND (which holds the about-to-execute command), BASH_SOURCE, BASH_LINENO, and FUNCNAME arrays, it provides enough infrastructure to build a full step-through debugger entirely in Bash. The bashdb project does exactly this.
Bash's tab completion system is Turing-complete¶
The complete and compgen builtins, combined with programmable completion functions, form a system powerful enough to implement arbitrary computation. Modern completion scripts for tools like git and docker run hundreds of lines of Bash to generate context-aware suggestions, effectively making the tab key trigger a mini-program.