Skip to content

Variables & Quoting Cheat Sheet

Quoting rules and variable types — the foundation of bug-free bash scripts.

Variable Assignment

name="hello"         # no spaces around =
count=42
path="/home/user"
arr=(one two three)  # indexed array

No spaces around =

name = "hello" is a syntax error — bash sees name as a command.

Quoting Types

Quote Effect
"..." Allows $var and $(cmd) expansion; prevents glob and word-splitting
'...' Completely literal — no expansion of any kind
No quotes Word-splitting and glob expansion happen
name="Ada Lovelace"
echo $name         # Ada Lovelace (lucky — splits into two words if passed to cmd)
echo "$name"       # Ada Lovelace (correct — one argument)
echo '$name'       # $name        (literal)

Rule: Quote every variable reference: "$var", "$@", "${arr[@]}".

Special Variables

Variable Meaning
$0 Script name
$1$9 Positional arguments
${10} Arguments beyond 9 (braces required)
$# Number of arguments
$@ All arguments (separately quoted as "$@")
$* All arguments as one string ("$*")
$? Exit status of last command
$$ PID of current shell
$! PID of last background process
$_ Last argument of previous command
$- Current shell option flags

Environment Variables

export VAR="value"        # make available to child processes
printenv                  # list all env vars
env                       # list all env vars
unset VAR                 # remove a variable

Common environment variables:

Variable Typical value
$HOME /home/user
$PATH /usr/local/bin:/usr/bin:/bin
$USER user
$SHELL /bin/bash
$PWD Current directory
$EDITOR vim
$LANG en_US.UTF-8
$TERM xterm-256color

$@ vs $*

args() { for a in "$@"; do echo "[$a]"; done; }
args "hello world" "foo"    # "$@": ["hello world"] ["foo"]
# vs
args2() { for a in "$*"; do echo "[$a]"; done; }
args2 "hello world" "foo"   # "$*": ["hello world foo"]

Always use "$@" when passing arguments to another command.

Command Substitution

today=$(date +%Y-%m-%d)          # modern syntax — use this
today=`date +%Y-%m-%d`           # old backtick syntax — avoid
count=$(ls | wc -l)

Arithmetic

result=$(( 5 + 3 ))      # arithmetic expansion
(( count++ ))             # increment (exit code 0 if non-zero result)
(( count += 5 ))          # compound assignment
let "x = 5 * 3"          # older syntax, avoid

(( 0 )) returns exit code 1

(( count-- )) when count is 1 sets it to 0, which returns exit code 1 — and with set -e, this exits the script. Use (( count-- )) || true to guard against this.

Variable Scoping

GLOBAL="global"

my_func() {
    local LOCAL="local"       # visible only in this function
    GLOBAL="modified"         # modifies the global
}

my_func
echo "$GLOBAL"   # modified
echo "$LOCAL"    # empty — not visible outside function

readonly Variables

readonly MAX=100
readonly -a COLORS=("red" "green" "blue")
MAX=200          # error: cannot assign to readonly variable

Related: bash-parameter-expansion