Variables & Quoting
Variables are how a shell script remembers things: a filename, a server name, the output of a command, or a number you do math on later. In Bash (the shell that ships as the default login shell on Ubuntu), variables look simple, but they have two famous traps for beginners — the spacing rules around the = sign, and the difference between single and double quotes. Getting quoting wrong is the single most common source of broken scripts in real DevOps work, so this page treats it as a first-class skill, not an afterthought.
Assigning a variable (no spaces!)
A variable is just a name that points to a value. You create one by writing the name, an equals sign, and the value — with no spaces around the =.
name="ubuntu-web-01"
count=5
log_dir="/var/log/nginx"
The “no spaces” rule trips up almost everyone. Bash treats a space as a separator between commands and arguments, so if you add spaces it thinks you are trying to run a program.
name = "ubuntu-web-01"
Output:
Command 'name' not found, did you mean:
command 'fame' from snap fame
Try: sudo apt install <deb name>
Bash saw name and assumed it was a command to run. The fix is simply to remove the spaces: name="ubuntu-web-01".
Variable names are case-sensitive and may only contain letters, digits, and underscores, and must not start with a digit. By strong convention, your own script variables are lowercase (
log_dir), while UPPERCASE is reserved for environment variables likePATHandHOME. Following this avoids accidentally overwriting an important system variable.
Using a variable: $VAR vs ${VAR}
To read a variable’s value, put a $ in front of its name. This is called expansion — Bash replaces $name with whatever the variable holds before running the command.
name="ubuntu-web-01"
echo "Deploying to $name"
Output:
Deploying to ubuntu-web-01
The curly-brace form ${name} does exactly the same thing, but it lets you mark exactly where the variable name ends. You need it whenever a letter, digit, or underscore comes right after the variable, because Bash would otherwise read those characters as part of the name.
env="prod"
echo "Backup file: backup_$env_2026.tar.gz"
echo "Backup file: backup_${env}_2026.tar.gz"
Output:
Backup file: backup_.tar.gz
Backup file: backup_prod_2026.tar.gz
In the first line Bash looked for a variable called env_2026 (which does not exist, so it expanded to nothing). The braces in the second line say “the variable is env, stop here”, which is what we wanted.
When to use which: use the plain $name form for readability in everyday cases, and reach for ${name} whenever the variable touches other text, or when you use Bash’s advanced expansions like ${name:-default} and ${#name} (those always need braces).
Single vs double quotes — the rule that matters most
Quotes tell Bash how literally to take the text inside them. This is the part to truly understand, because it decides whether your variables get expanded at all.
| Quote style | Expands $variables? | Expands $(commands)? | When to use |
|---|---|---|---|
"double quotes" | Yes | Yes | The default. Use this almost always. |
'single quotes' | No (taken literally) | No (taken literally) | When you want the text exactly as typed. |
| no quotes | Yes, then word-splits | Yes, then word-splits | Avoid for variable values. |
A quick demonstration with one variable holding a value that contains a space:
greeting="Hello there"
echo "$greeting"
echo '$greeting'
Output:
Hello there
$greeting
Double quotes expanded the variable to its value. Single quotes printed the literal characters $greeting. Single quotes are perfect when you genuinely want a dollar sign in your text — for example, writing an awk program or a price like '$5/month'.
Always quote your variables
The golden rule: always wrap variable expansions in double quotes — write
"$file", not$file. An unquoted variable is split on spaces and tabs (called word-splitting) and any*or?is treated as a filename wildcard (globbing). This causes silent, dangerous bugs, especially with file paths.
Here is the bug in action. Imagine a file whose name contains a space, which is extremely common on real systems.
file="My Report.txt"
touch "$file"
rm $file
Output:
rm: cannot remove 'My': No such file or directory
rm: cannot remove 'Report.txt': No such file or directory
Because $file was unquoted, Bash split it into two arguments, My and Report.txt, and rm tried to delete two files that do not exist. The correct, safe version quotes the variable so it stays a single argument:
file="My Report.txt"
rm "$file"
The same trap appears with command output, loops over filenames, and any path that might contain spaces — quoting fixes all of them at once.
Combining variables and command output
Variables become powerful when you store the result of a command. You capture command output with $(...), then quote it like any other value.
today=$(date +%F)
backup="/var/backups/db-${today}.sql"
echo "Writing backup to: $backup"
Output:
Writing backup to: /var/backups/db-2026-06-15.sql
This pattern — build a path from a variable, then quote it everywhere — is the backbone of backup scripts, log rotation, and deployment tooling on Ubuntu servers.
Best practices
- Never put spaces around
=when assigning:key="value", notkey = "value". - Quote every expansion in double quotes by default:
"$var","${path}/file","$(command)". - Use
${var}braces when the variable is next to other characters or when using expansions like${var:-default}. - Use single quotes only when you deliberately want the text taken literally (no
$expansion). - Lowercase your own variable names; leave UPPERCASE for environment variables to avoid clobbering
PATH,HOME, and friends. - Give a safe default for variables that might be empty with
${name:-fallback}, so a missing value never breaks a path or command. - Run shellcheck (
sudo apt install shellcheck) on your scripts — it flags unquoted variables and quoting mistakes automatically.