← Back to Bash Course | Chapter 2: Variables & Types | Lesson 10 of 13

Special Variables

Bash automatically fills in a handful of special variables with useful information about the script itself and how it was called, without you ever assigning them.

$0, $1, $2... and $#

$0 holds the name the script was invoked with, while $1, $2, and so on hold the individual arguments passed to it in order. $# tells you how many positional arguments were supplied, which is useful for validating input before using it.

Warning: set -- alpha beta gamma is used here to simulate command-line arguments inside the script for demonstration.

Example: $0, $1, $2... and $#

bash
#!/bin/bash
set -- alpha beta gamma
echo "Script name: $0"
echo "First arg: $1, second arg: $2"
echo "Total args: $#"

$@ vs $* Unquoted

Unquoted, both $@ and $* expand to all positional parameters separated by spaces and are then subject to word splitting, so they behave identically in that form. The difference only shows up once you quote them.

Example: $@ vs $* Unquoted

bash
#!/bin/bash
set -- one two three
for arg in $@; do
    echo "unquoted \$@: $arg"
done

"$@" vs "$*" Quoted

Quoted as "$@", each positional parameter expands as its own separate, fully preserved word (spaces inside an argument stay intact). Quoted as "$*", all parameters are joined into a single string separated by the first character of IFS (a space by default).

Warning: When forwarding arguments to another command, "$@" is almost always the correct choice.

Example: "$@" vs "$*" Quoted

bash
#!/bin/bash
set -- "first arg" "second arg"
echo "Using \"\$@\":"
for arg in "$@"; do
    echo "  [$arg]"
done
echo "Using \"\$*\": [$*]"

$?, $$, and Other Specials

$? gives the exit status of the most recently completed command, and $$ gives the process ID of the currently running shell itself, which is often used to build unique temporary filenames. These specials are read-only and update automatically as the script runs.

Example: $?, $$, and Other Specials

bash
#!/bin/bash
echo "This script's PID is $$"
true
echo "Exit status after true: $?"
tmpfile="/tmp/scratch_$$.txt"
echo "Would use temp file: $tmpfile"
Common Mistakes
  1. Confusing $@ and $*; unquoted they behave the same, but quoted, "$@" expands each argument as a separate word (preserving spaces inside each), while "$*" joins all arguments into one single string.
  2. Assuming $0 is always just the script's filename; it is actually whatever string was used to invoke the script, which can be a relative path, absolute path, or just a name depending on how it was called.
  3. Forgetting $$ is the current shell's own process ID, not the ID of the last background command started (that is $!, a related but different special variable).
Chapter Summary
  • $0 is the name/path used to invoke the script; $1, $2, ... are the positional arguments passed to it.
  • $# is the count of positional arguments; $@ and $* both expand to all of them, but differ when quoted.
  • "$@" is almost always what you want when forwarding arguments, since it preserves each argument as a separate word even if it contains spaces.
  • $? is the exit status of the last command; $$ is the current process's PID.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.