Bash Special Variables Reference

Every Bash special variable ($?, $$, $!, $@, etc.) with meaning and scope.

Searchable reference for Bash special and automatic variables — $?, $$, $!, $0, $#, $@, $*, $-, $_, $BASHPID, $RANDOM, $LINENO, $IFS, $PIPESTATUS — each with its meaning, scope, and a usage example.

What is the difference between $@ and $*?

Both expand to all positional parameters. Unquoted they behave the same, but quoted they differ: "$@" expands to each argument as a separate quoted word, preserving spaces, while "$*" joins them into one word separated by the first character of IFS. In loops always use "$@".

Bash exposes a set of special variables the shell maintains for you — exit status, process IDs, positional parameters, and more. Misreading them, or forgetting that $? is overwritten immediately, causes subtle scripting bugs. This tool is a searchable reference of every special and automatic variable with its meaning, scope, and a usage example.

How it works

These variables fall into a few families:

  • Status & control$? (last exit code), $- (current shell flags), PIPESTATUS (per-command status of the last pipeline).
  • Process IDs$$ (shell PID), $BASHPID (current process PID), $! (PID of the last background job), $PPID (parent PID).
  • Positional parameters$0 (script name), $1$9, ${10}, $# (count), $@ and $* (all args), $_ (last arg of previous command).
  • Shell internals$IFS (field separator), $RANDOM, $SECONDS, $LINENO, $FUNCNAME, $BASH_SOURCE.

Worked example

#!/usr/bin/env bash
process() {
  grep -q "$1" "$2"          # may succeed or fail
  local status=$?            # capture immediately
  if (( status != 0 )); then
    echo "not found (exit $status)" >&2
  fi
}

for arg in "$@"; do          # quoted "$@" keeps each arg intact
  process "$arg" config.txt
done

false | true
echo "${PIPESTATUS[0]}"      # 1 — the failing 'false', not the pipeline's 0

Notes

  • Always quote "$@" in loops and when forwarding arguments; unquoted $@ re-splits on whitespace and breaks filenames with spaces.
  • $? is clobbered by the next command — including the test in an if. Save it to a local variable the moment you need it.
  • $$ stays constant across subshells (it is the main shell’s PID); reach for $BASHPID when you genuinely need the current subshell’s PID.
  • $RANDOM returns a value 0–32767 and is not cryptographically secure; use /dev/urandom for security-sensitive randomness.