Three quoting modes
'single'— fully literal. No expansion, no escapes."double"— variables and command substitutions expand; single chars (\$,\") escapable.no quotes— word-split and glob-expand. Almost never what you want.
The 90% rule: quote your variables
name="Pippa Choi"
echo $name # Pippa Choi (looks fine but two args were passed)
echo "$name" # Pippa Choi (one arg)
for f in $files; do # word-splits the value of $files
for f in "$files"; do # one iteration regardlessIf the unquoted form passes more args, edge cases (filenames with spaces, empty values) silently break. Always "$var" unless you specifically want word splitting.
Default values
: "${PORT:=8000}" # set if unset
echo "${USER:-anon}" # use 'anon' if unset; don't change USER
: "${REQUIRED:?must be set}" # error and exit if unsetThe leading : is the no-op command — we just want the side effect of expansion. The full parameter expansion table lives in the epilogue track.
Read user input
read -rp 'Name: ' name
echo "hi $name"
# -r preserves backslashes; always use it
# -s for silent (passwords)Local vs global
Inside a function, local var=value keeps the variable private to the function. Without local, the assignment leaks to the outer scope. POSIX sh has no local; bash/zsh do. Most function bugs come from forgetting it.