DEV Community

SEN LLC
SEN LLC

Posted on

A Bash Special Variables Reference — $@ vs $*, Parameter Expansion, and 50+ More

A Bash Special Variables Reference — $@ vs $*, Parameter Expansion, and 50+ More

"$@" and "$*" look identical but behave completely differently in a for loop. ${var:-default} assigns a default without mutation, ${var:=default} with mutation. ${var%pattern} strips from the end, ${var#pattern} from the start. 52 entries, bilingual explanations, examples you can copy-paste, all searchable.

Bash has dozens of magic variables and parameter expansion forms that experienced shell programmers use daily but that new users don't know exist. man bash is 40,000 words. A faster lookup tool exists, but none of them cover Japanese speakers, and most don't include the "why does this exist" context.

🔗 Live demo: https://sen.ltd/portfolio/bash-special-vars/
📦 GitHub: https://github.com/sen-ltd/bash-special-vars

Screenshot

Features:

  • 52 entries in 6 categories
  • Positional parameters ($0, $1-$9, $@, $*)
  • Special parameters ($#, $?, $$, $!, $_, $-)
  • Parameter expansion (${var:-default}, ${var%pattern}, etc.)
  • Environment variables ($HOME, $PATH, $BASH_VERSION, etc.)
  • Array syntax (${arr[@]}, ${#arr[@]})
  • Process info ($LINENO, $FUNCNAME, $BASH_SOURCE)
  • Bilingual Japanese / English descriptions
  • Example + expected output per entry
  • Dark / light theme
  • Zero dependencies, 46 tests

The "$@" vs "$*" distinction

This is the most common Bash gotcha:

# "$@" expands to: "arg1" "arg2" "arg3" — separate quoted strings
for arg in "$@"; do echo "$arg"; done

# "$*" expands to: "arg1 arg2 arg3" — single concatenated string
for arg in "$*"; do echo "$arg"; done
Enter fullscreen mode Exit fullscreen mode

The first form iterates over three arguments. The second form iterates once over a single string. When you want to forward arguments to another command unchanged, you want "$@". When you want to join arguments for logging, you want "$*".

Without quotes, $@ and $* behave identically — both split on IFS. The quotes matter.

Parameter expansion cheatsheet

13 expansion forms, each solving a different problem:

Form Behavior
${var} Same as $var, but allows adjacent characters
${var:-default} If var unset/empty, use default (no mutation)
${var:=default} If var unset/empty, assign default
${var:?error} If var unset/empty, print error and exit
${var:+alt} If var set, use alt (opposite of :-)
${var:offset:length} Substring extraction
${#var} String length
${var#pattern} Strip shortest match from start
${var##pattern} Strip longest match from start
${var%pattern} Strip shortest match from end
${var%%pattern} Strip longest match from end
${var/old/new} Replace first match
${var//old/new} Replace all matches

Pattern strippers (#, ##, %, %%) are how you parse filenames in pure Bash without sed:

file="/path/to/archive.tar.gz"
echo "${file##*/}"     # archive.tar.gz  (basename)
echo "${file%/*}"      # /path/to        (dirname)
echo "${file%.*}"      # /path/to/archive.tar  (strip last ext)
echo "${file%%.*}"     # /path/to/archive      (strip all ext)
Enter fullscreen mode Exit fullscreen mode

Learning these four saves shelling out to basename / dirname / sed in every script.

$? vs $!

Two process-related variables that sound similar:

  • $? — Exit status of the most recently completed foreground command
  • $! — PID of the most recently started background process
./long-running &
bg_pid=$!
./quick-check
echo "quick-check exit: $?"
wait $bg_pid
echo "long-running exit: $?"
Enter fullscreen mode Exit fullscreen mode

The order matters: $? always refers to the last foreground command. Save it to a variable if you need to refer to it later.

Less-known but useful

  • $_ — Last argument of the previous command. mkdir foo && cd $_ saves typing cd foo.
  • $LINENO — Current line number in the script. Useful in error messages.
  • $BASH_SOURCE[0] — Path to the current script file, even when sourced. More reliable than $0.
  • $RANDOM — A random 15-bit integer. Reseeds every read.
  • $SECONDS — Seconds since shell started. Useful for simple benchmarks.
  • $PS1 — Primary prompt string. Changing it customizes your prompt.
  • $IFS — Internal Field Separator. Changing to IFS=, lets you parse CSV with read -a.

Series

This is entry #67 in my 100+ public portfolio series.

Top comments (0)