Script foundations
Use variables and quoting
Store values and expand them without accidental word splitting or wildcard expansion.
10 minute lesson
Shell variables hold text. You assign with name=value — no spaces around the = — and read the value back with $name.
Two quoting rules do most of the work in shell. Double quotes preserve one argument while still expanding variables inside. Single quotes preserve literal text, with no expansion at all:
project='My Notes'
printf '%s\n' "$project" # My Notes
printf '%s\n' '$project' # $project
What unquoted expansion does
Here is the classic failure. Compare quoted and unquoted expansion:
project='My Notes'
printf '<%s>\n' "$project"
# <My Notes>
printf '<%s>\n' $project
# <My>
# <Notes>
The quoted command prints one value. The unquoted command passes two words: after Bash expands $project, it splits the result on spaces, tabs, and newlines before the command runs. This is word splitting. printf received two separate arguments and applied the format to each one.
Now put the same mistake next to a destructive command:
file='old notes.txt'
rm $file
# rm: cannot remove 'old': No such file or directory
# rm: cannot remove 'notes.txt': No such file or directory
rm was asked to delete two files named old and notes.txt. If a file named old had existed, it would be gone now. rm "$file" removes exactly the one file you meant.
Wildcards expand too
Word splitting is not the only surprise. After splitting, Bash performs pathname expansion on unquoted results, so a value containing * gets matched against files in the current directory:
pattern='*'
printf '%s\n' $pattern # prints every filename in the directory
printf '%s\n' "$pattern" # *
A variable you thought held a harmless string just enumerated your files.
The habit to build
Quote every expansion unless you can explain why you want splitting: "$project", "$1", "$HOME/backups". Double-quoting a variable you want treated as one value is never wrong.
And never rely on filenames having no spaces, wildcard characters, or leading dashes. Someone eventually creates My Notes (final) *.txt, and unquoted scripts break exactly then. ShellCheck flags every risky unquoted expansion — we run it at the end of the course.
Lesson completed