Variables and Quoting
Scope: assignment, expansion, and the quoting rules that prevent most Bash bugs. All examples were run with Bash 5.2.
Assignment: no spaces
name="Ada"
age=36
No spaces around =. name = "Ada" tries to run a command
called name — the single most common beginner error, and the
error message won't hint at the cause.
Expansion: $name
echo "Hello, $name"
echo "Next year: $((age + 1))"
$name/${name}— the value. Braces disambiguate:"${name}s"vs"$names"(different variables!).$((...))— arithmetic.$(...)— command substitution (output of a command).
The quoting rules (memorize these)
| Quote | Expands $var? |
Expands $(cmd)? |
Use for |
|---|---|---|---|
"double" |
Yes | Yes | Almost everything |
'single' |
No | No | Literal text |
| none | Yes, then word-split | Yes | Almost never |
file="my notes.txt"
cat "$file" # one argument: my notes.txt ✓
cat $file # TWO arguments: my, notes.txt ✗
Unquoted variables get word-split on spaces — the classic source of "works until a filename has a space." Rule: quote every expansion, every time, unless you specifically want splitting or globbing.
Single quotes are for literals: 'Cost: $5' prints Cost: $5
with no expansion.
Special variables
| Variable | Meaning |
|---|---|
$0 |
Script name |
$1, $2, ... |
Positional arguments |
$# |
Number of arguments |
$@ |
All arguments (quoted: "$@") |
$? |
Exit code of the last command |
$$ |
This script's process ID |
#!/usr/bin/env bash
echo "Script: $0"
echo "Args: $#"
echo "First: $1"
Run ./args.sh one two → Script: ./args.sh, Args: 2,
First: one.
Always "$@", never $@ or $*, when passing arguments
along — only "$@" preserves arguments containing spaces.
Exports and environment
export EDITOR="nano" # visible to child processes
EDITOR="nano" # visible only in this shell
export marks a variable for child processes. Scripts usually
don't need it unless calling programs that read the variable.
readonly and defaults
readonly VERSION="1.0" # cannot be reassigned
echo "${MISSING:-default}" # "default" if unset
echo "${MISSING:?must be set}" # error out if unset
${var:-default} is handy for optional settings;
${var:?message} fails fast on required ones.
Check
- [ ] You write
name="x"with no spaces and can say why - [ ] You quote every
"$variable"by reflex - [ ] You can explain
"$@"vs$*in one sentence
Next: Control Flow.