Variables & Quoting¶
Quoting is the most common source of subtle bugs in bash — a single missing quote can turn a command that handles one file into one that silently handles dozens, or breaks on filenames with spaces.
Learning Objectives¶
- Declare, assign, and read shell variables
- Understand the difference between local, environment, and special variables
- Apply the three quoting styles: single quotes, double quotes, no quotes
- Use special variables:
$0,$#,$@,$?,$$ - Export variables to child processes
Variables¶
Declaring and Reading Variables¶
# No spaces around = in assignments
count=42
greeting="Hello, World"
pi=3.14159
# Read a variable
echo "$count" # always quote variable references
echo "${count}" # braces make the boundary explicit
No spaces around =
count = 42 is a syntax error in bash — bash sees count as a command with arguments = and 42. Variable assignment requires name=value with no spaces.
Environment Variables¶
Environment variables are inherited by child processes. View them with env or printenv:
echo "$HOME" # your home directory
echo "$PATH" # directories searched for commands
echo "$USER" # your username
echo "$PWD" # current directory (same as pwd)
echo "$SHELL" # your default shell
Export a variable to make it available to child processes:
Without export, the variable exists only in the current shell:
Special Variables¶
| Variable | Meaning |
|---|---|
$0 |
Name of the script |
$1 … $9 |
Positional arguments |
$# |
Number of arguments |
$@ |
All arguments as separate words |
$* |
All arguments as a single word |
$? |
Exit status of the last command |
$$ |
PID of the current shell |
$! |
PID of the last background process |
#!/usr/bin/env bash
echo "Script name: $0"
echo "First arg: $1"
echo "All args: $@"
echo "Arg count: $#"
Running bash myscript.sh foo bar baz:
Quoting¶
No Quotes — Word Splitting and Glob Expansion Happen¶
file="my report.txt"
cat $file # WRONG: cat sees "my" and "report.txt" as separate args
cat "$file" # CORRECT: cat sees one argument with a space in it
Unquoted variables also undergo glob expansion:
pattern="*.txt"
ls $pattern # expands the glob — lists all .txt files
ls "$pattern" # treats *.txt as a literal filename
Double Quotes — Variables and Command Substitution Expand, Globs Don't¶
name="Nikhil"
echo "Hello, $name" # Hello, Nikhil
echo "Files: $(ls | wc -l)" # Files: 23
echo "Literal: \$name" # Literal: $name
Double quotes protect against word splitting and glob expansion while still allowing variable and command substitution.
Single Quotes — Everything Is Literal¶
echo 'Hello, $name' # Hello, $name (no expansion)
echo 'Cost: $5.00' # Cost: $5.00
echo 'No $(expansion) here' # No $(expansion) here
Single quotes prevent all expansion and substitution.
The rule to follow
Quote every variable reference with double quotes unless you have a specific reason not to. "$var", not $var. "$@", not $@. This prevents word-splitting bugs on filenames with spaces.
Command Substitution¶
Run a command and capture its output into a variable:
$() vs backticks
The modern syntax is $(command). Backtick syntax `command` is older and harder to nest. Use $() in all new scripts.
Variable Defaults and Errors¶
# Use a default if the variable is unset or empty
echo "${name:-"stranger"}"
# Assign a default if the variable is unset
: "${name:="stranger"}"
# Error out if the variable is unset
: "${REQUIRED_VAR:?Variable REQUIRED_VAR must be set}"
Common Mistakes¶
Quoting $@ vs $*
Use "$@" (double-quoted) to pass arguments to another command. "$@" expands to a list of separately quoted words. "$*" joins all arguments with a space into one string — which breaks if your arguments contain spaces.
Variable names are case-sensitive
$PATH and $path are different variables. Convention: use uppercase for environment variables and exported variables; use lowercase for local script variables.
Practice Exercises¶
Warm-Up (run and observe)¶
- Create a variable
city="New York". Tryecho $cityandecho "$city". What is the difference when the value has a space? - Run
echo $?immediately after a successfulls. Run it afterls /nonexistent. What values do you see? - Run
echo $$. Then open another terminal and runecho $$. What do the numbers represent?
Main (write a short script)¶
Create ~/scripts/greet.sh that accepts a name as an argument and greets the user:
#!/usr/bin/env bash
set -euo pipefail
NAME="${1:?Usage: $0 <name>}"
TIMESTAMP=$(date +"%Y-%m-%d %H:%M")
echo "Hello, $NAME!"
echo "The time is: $TIMESTAMP"
echo "You are running: $0"
echo "Shell: $SHELL"
Test it: bash ~/scripts/greet.sh "Ada Lovelace"
Stretch¶
- What is the difference between
$@and$*when they are unquoted? When they are double-quoted? Write a script that demonstrates the difference with a filename containing a space. - How do you append to a variable without overwriting it? (e.g., add a directory to
PATH) - Research
declare -r VAR=value. What does-rdo? What happens if you try to reassign a readonly variable?
Interview Questions¶
- What happens if you reference a variable that was never assigned?
Show answer
By default, bash treats unset variables as empty strings — no error. With set -u (or set -euo pipefail), referencing an unset variable causes the script to exit with an error. This is why set -u is recommended in production scripts.
- What is the difference between
$@and$*?
Show answer
When double-quoted: "$@" expands to a list of individually quoted words ("$1" "$2" "$3"). "$*" expands to a single string with all arguments joined by the first character of IFS (usually a space: "$1 $2 $3"). Use "$@" when passing arguments to other commands to preserve arguments that contain spaces.
- Why is
count = 42a syntax error in bash?
Show answer
Bash sees count as a command name and = and 42 as its arguments. Variable assignment requires name=value with no spaces. The parser treats anything before the first = (with no spaces) as the variable name.
day02-part2-stream-editing | day03-part2-user-input-expansion