Skip to content
C.W.K.
Stream
Lesson 02 of 12 · published

Variables and Quoting

~13 min · variables, quoting, expansion

Level 0Window Tourist
0 XP0/95 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete

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.

Preserve one scalar argument first

name="Alice Lee"
echo $name           # Alice Lee  (looks fine but two args were passed)
echo "$name"         # Alice Lee  (one arg)
for f in $files; do  # word-splits the value of $files
for f in "$files"; do # one iteration regardless

If 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 unset

The 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.

Use arrays to preserve multiple value boundaries

name="Project Alpha"
echo $name             # two arguments after splitting
echo "$name"           # one argument
for f in $files; do    # splits one string
for f in "$files"; do  # always one iteration

Unquoted expansion changes argument boundaries through whitespace and glob characters. Pass scalar filenames and user input as "$var"; store multiple values in an array and expand with "${arr[@]}".

Quoting is argument design

The shell turns text into a command and arguments. Quotes decide where one argument ends. Print boundaries with printf '<%s>\n' "$@" when debugging.

An empty value still has meaning

A quoted empty variable becomes one empty argument, while an unquoted empty variable can disappear. Programs may distinguish “empty option value” from “option absent,” so quoting preserves empty-value semantics as well as spaces.

Code

Always quote·bash
#!/usr/bin/env bash
set -euo pipefail
files=("a file.txt" "b.txt")
for f in "${files[@]}"; do
  ls -l -- "$f"
done

External links

Exercise

Make a file with space.txt. Try f='with space.txt'; cat $f and watch it fail. Then cat "$f" and watch it work. Install ShellCheck and run it on any script in your home.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.