LFCA 57 ๐ง Shell Scripting โ Variables and Quoting
In the previous chapter, you wrote a Hello World script, made it executable, and passed arguments to it. Those commands were static โ the same words, the same paths, every time the script ran. Real scripts are not static. They accept filenames that change, dates that vary, and user input that arrives at runtime. Variables are how shell scripts hold that data. Quoting is how they control what the shell does with it.
The relationship between variables and quoting is the single most important concept to understand about Bash. A variable without quotes is a variable that the shell will split at every space and expand into every matching filename. That behavior is occasionally useful and frequently catastrophic. A variable with the wrong kind of quotes is a variable that refuses to expand at all, or one that expands into something you did not intend. The LFCA exam tests this under the “Command Line” competency, and the failure modes are exactly what the exam’s scenario questions are designed to catch .
This chapter covers variable creation, reference syntax, the three quoting mechanisms, the exact order in which the shell processes a command, and the rules that determine when a variable expands and when it does not.
Key point: The shell processes a command in five stages: parsing, parameter and command substitution, word splitting, filename generation, and quote removal . Quoting is not a single operation โ it is the set of rules that determines which of those stages apply to a given piece of text. Single quotes suppress all of them except quote removal. Double quotes suppress some but allow parameter and command substitution. Backslashes suppress exactly one character. The exam tests the differences, not the definitions.
Why quoting exists
The shell is not a programming language in the conventional sense. It is a text processor that decides what to do based on characters. Spaces separate arguments. Asterisks match filenames. Dollar signs introduce variables. Semicolons end commands. Every one of these characters has a special meaning by default .
Quoting is the act of taking that special meaning away. When you quote a character, you tell the shell: treat this literally, do not interpret it . The choice of quoting mechanism determines how much you take away and how long you take it away for.
The reason this matters is that variables hold text, and that text often contains characters the shell would otherwise interpret. A variable holding a filename with a space in it โ /home/user/My Documents โ is the canonical example. Unquoted, the shell sees two words: /home/user/My and Documents. The command receives two arguments instead of one. Quoted, the shell sees one word, and the command receives the filename correctly .
The same problem applies to asterisks. A variable holding *.txt unquoted expands to every text file in the current directory. Quoted, it stays *.txt . The variable does not know what it holds. Quoting is how you tell the shell what to do with it.
a. Variable Assignment and Reference
Variables in Bash are created with the name=value syntax. There are no spaces around the equals sign. NAME="Alice" is correct. NAME = "Alice" is three separate words: a command named NAME, an argument =, and an argument Alice โ which fails with “command not found” .
#!/bin/bash
NAME="Alice"
AGE=30
echo "$NAME is $AGE years old"
The value on the right side of the equals sign undergoes tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, and quote removal โ but not word splitting and not filename expansion . This is a critical distinction. The value is not re-split into words after substitution. MESSAGE="Hello $NAME" stores the string Hello Alice as a single value, even though it contains a space.
To reference a variable, prefix its name with $. $NAME expands to the value. ${NAME} does the same thing but is unambiguous when the variable is adjacent to other text. In echo "${NAME}world", the braces make it clear that the variable is NAME, not NAMEworld . The braces are optional when followed by whitespace or punctuation, but they are always safe.
NAME="Alice"
echo "$NAME" # Alice
echo "${NAME}world" # Aliceworld
echo "$NAMEworld" # empty โ variable NAMEworld does not exist
There are three categories of variables in Bash: shell variables, which exist only in the current shell; environment variables, which are inherited by child processes; and special parameters, which are set by the shell itself and cannot be assigned . The export command converts a shell variable into an environment variable . Without export, a variable set in a script is invisible to any command or subprocess the script launches.
NAME="Alice" # shell variable only
export NAME # now inherited by child processes
export NAME="Alice" # set and export in one statement
The unset command removes a variable entirely . readonly NAME prevents reassignment . declare -n ref=$1 creates a nameref โ a reference to another variable whose name is passed as an argument, commonly used in functions .
b. Single Quotes: Total Suppression
Single quotes are the most absolute form of quoting. Everything between the opening ' and the closing ' is taken literally. Not a single character retains its special meaning .
echo 'Hello $NAME' # Hello $NAME โ no expansion
echo 'The price is $5' # The price is $5 โ literal dollar sign
echo '*.txt' # *.txt โ no filename expansion
echo 'It'\''s here' # It's here โ see below for the escape
Inside single quotes, $, \, `, *, ?, and every other metacharacter is just a character. The shell performs only quote removal โ it deletes the single quotes and passes the literal content to the command . This is the safest quoting for text that should never be interpreted, such as strings containing dollar signs, backslashes, or wildcard characters meant literally.
The one thing you cannot do inside single quotes is include a single quote. The shell has no escape mechanism inside single quotes โ a backslash is just a backslash. To embed a single quote, you must close the quotes, add an escaped quote, and reopen: 'It'\''s' . This is read as 'It' + \' + 's', producing It's.
Single quotes are the right choice for literal strings, regular expressions passed to tools like grep, and any text where expansion is not wanted .
c. Double Quotes: Selective Suppression
Double quotes are less absolute. They suppress the special meaning of most characters, but they allow parameter expansion ($), command substitution (` or $(...)), arithmetic expansion ($((...))), and backslash escaping of specific characters .
NAME="Alice"
echo "Hello $NAME" # Hello Alice โ expansion happens
echo "The price is \$5" # The price is $5 โ backslash escapes $
echo "Today is $(date)" # Today is <date output> โ command runs
echo "Path: /home/$USER" # Path: /home/<username>
The four characters that retain special meaning inside double quotes are $, `, \, and " itself . The backslash inside double quotes only escapes those four characters (and a newline). A backslash before any other character is taken literally โ it remains in the output .
Double quotes are the default quoting for variable references. The rule of thumb from decades of shell practice: “variable references should almost always be contained within double quotes; failure to do so is the main reason why scripts fail on filenames which contain spaces” . If you quote nothing else in a script, quote every $VAR and $(command) in double quotes.
The combination of double quotes and $@ is a special case worth knowing. "$@" expands each positional parameter as a separate word, preserving the boundaries between them even if individual parameters contain spaces . Unquoted $@ splits each parameter at its internal spaces. This is the correct way to forward arguments to another command.
Complete Example Session
This session builds a script that demonstrates each quoting mechanism, the difference between quoted and unquoted expansion, and the failure cases that quoting prevents.
#!/bin/bash
# quoting-demo.sh - Demonstrates Bash quoting behavior
# ============================================
# PART 1: THE UNQUOTED VARIABLE TRAP
# ============================================
FILENAME="My Document.txt"
touch "$FILENAME" # creates a file with a space in the name
ls $FILENAME # โ tries to list two files: "My" and "Document.txt"
ls "$FILENAME" # โ
lists the one file
# ============================================
# PART 2: THE GLOB EXPANSION TRAP
# ============================================
PATTERN="*.txt"
echo $PATTERN # โ expands to every .txt file in the directory
echo "$PATTERN" # โ
prints *.txt literally
echo '$PATTERN' # โ
prints $PATTERN literally
# ============================================
# PART 3: THE SINGLE QUOTE SUPPRESSION
# ============================================
NAME="Alice"
echo 'Hello $NAME' # prints: Hello $NAME
echo "Hello $NAME" # prints: Hello Alice
# ============================================
# PART 4: THE DOUBLE QUOTE EXPANSION
# ============================================
echo "Today is $(date +%Y-%m-%d)" # command substitution works
echo 'Today is $(date +%Y-%m-%d)' # prints the command literally
# ============================================
# PART 5: THE BACKSLASH ESCAPE
# ============================================
echo "The price is \$5" # prints: The price is $5
echo "The price is $5" # prints: The price is 5 (empty var $5)
# ============================================
# PART 6: THE BRACE DISAMBIGUATION
# ============================================
FILE="report"
echo "${FILE}port" # prints: reportport
echo "$FILEport" # prints: (empty) โ no variable FILEport
# ============================================
# PART 7: THE EXPORTED VARIABLE
# ============================================
export GREETING="Hello from parent"
bash -c 'echo $GREETING' # prints: Hello from parent
UNEXPORTED="not visible"
bash -c 'echo $UNEXPORTED' # prints: (empty)
# ============================================
# PART 8: THE POSITIONAL PARAMETER PRESERVATION
# ============================================
# Usage: ./script.sh "file one.txt" "file two.txt"
for arg in "$@"; do # โ
preserves spaces in each argument
echo "Argument: $arg"
done
for arg in $@; do # โ splits "file one.txt" into two arguments
echo "Argument: $arg"
done
# ============================================
# PART 9: THE SAFE VARIABLE ASSIGNMENT
# ============================================
CITIES="London ; Paris ; New York"
echo "$CITIES" # โ
one string: London ; Paris ; New York
echo $CITIES # โ multiple arguments to echo
# ============================================
# PART 10: THE READ WITH QUOTES
# ============================================
while IFS= read -r line; do # โ
preserves leading/trailing spaces
echo "Line: $line"
done < input.txt
The ten parts cover the unquoted variable trap, glob expansion, single quote suppression, double quote expansion, backslash escaping, brace disambiguation, variable export, positional parameter preservation, safe assignment, and safe read.
Quick Reference
The Quoting Mechanisms
| Mechanism | Suppresses | Allows | Use For |
|---|---|---|---|
| Unquoted | Nothing | Everything | Rarely โ only when splitting is wanted |
Single '...' | Everything | Nothing (literal) | Literal strings, regex, dollar signs |
Double "..." | Most things | $, `, \, " | Variable references, command substitution |
Backslash \x | Next character only | โ | Escaping one metacharacter |
The Expansion Order
| Stage | What Happens | Affected By |
|---|---|---|
| 1. Parsing | Split into words, detect quotes | ', ", \, spaces |
| 2. Parameter/command substitution | $VAR, $(cmd) expand | Double quotes allow, single suppress |
| 3. Word splitting | Split on IFS characters | Quoting suppresses |
| 4. Filename generation | *, ? expand | Quoting suppresses |
| 5. Quote removal | Quotes deleted | โ |
The Special Characters
| Character | Unquoted | Single Quoted | Double Quoted |
|---|---|---|---|
$ | expansion | literal | expansion |
* | glob | literal | literal |
` | command sub | literal | command sub |
\ | escape | literal | escapes $ \ “` |
| space | word split | literal | literal |
Best Practices
โ Do This:
FILENAME="My Document.txt" # โ
ls "$FILENAME" # โ
always quote variables
echo "${FILE}port" # โ
braces for clarity
export API_KEY # โ
export before use in subprocess
for arg in "$@"; do # โ
preserve argument boundaries
CITIES="London ; Paris ; New York" # โ
single value with semicolons
while IFS= read -r line; do # โ
safe file reading
โ Don’t Do This:
NAME = "Alice" # โ spaces around =
ls $FILENAME # โ word splitting on spaces
echo $PATTERN # โ glob expansion
echo "$FILEport" # โ ambiguous variable boundary
for arg in $@; do # โ splits arguments with spaces
CITIES=London ; Paris ; New York # โ semicolon ends assignment
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| “Command not found” on assignment | Spaces around = | Use NAME="value" |
| Filename with space fails | Unquoted variable | Quote every $VAR |
| Glob expands unexpectedly | Unquoted variable holding * | Quote or single-quote |
| Variable appears empty | Wrong name or not exported | Check spelling, use export |
$FILEport empty | Shell reads FILEport as name | Use ${FILE}port |
$@ splits arguments | Unquoted expansion | Use "$@" |
| Semicolon ends assignment | Unquoted value | Quote the whole value |
Real-World Examples
1. Safe Filename Handling
FILENAME="My Document.txt"
ls "$FILENAME"
2. Literal Dollar Sign
echo 'The cost is $5.00'
3. Command Substitution with Double Quotes
echo "Today is $(date)"
4. Brace Disambiguation
FILE="report"
echo "${FILE}port"
5. Export for Subprocess
export DATABASE_URL="postgres://localhost/db"
6. Forward Arguments Safely
for arg in "$@"; do echo "$arg"; done
7. Regex Passed to grep
grep '^[0-9]\{3\}$' file.txt
8. Multi-Word Value
CITIES="London ; Paris ; New York"
echo "$CITIES"
9. Safe File Reading
while IFS= read -r line; do echo "$line"; done < file.txt
10. Escape Inside Double Quotes
echo "The value is \"$VAR\""
Visual: Quoting Mechanisms
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ QUOTING MECHANISMS โ
โ โ
โ 'Single' โ Everything literal โ
โ $NAME stays $NAME โ
โ โ
โ "Double" โ $, `, \, " still work โ
โ $NAME becomes Alice โ
โ โ
โ \Backslash โ Next character literal โ
โ \$5 becomes $5 โ
โ โ
โ None โ Everything works โ
โ $NAME splits and globs โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Expansion Order
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ EXPANSION ORDER โ
โ โ
โ 1. Parsing โ
โ โโ Detect quotes, split words โ
โ โ
โ 2. Substitution โ
โ โโ $VAR โ value, $(cmd) โ output โ
โ (single quotes suppress this) โ
โ โ
โ 3. Word Splitting โ
โ โโ Split on spaces โ
โ (quoting suppresses this) โ
โ โ
โ 4. Filename Generation โ
โ โโ *.txt โ file1.txt file2.txt โ
โ (quoting suppresses this) โ
โ โ
โ 5. Quote Removal โ
โ โโ Delete the quote characters โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: The Unquoted Variable Trap
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ UNQUOTED VARIABLE TRAP โ
โ โ
โ FILENAME="My Document.txt" โ
โ โ
โ ls $FILENAME โ
โ โโ shell sees: ls My Document.txt โ
โ โโ TWO arguments: "My" and "Document.txt" โ
โ โโ โ error or wrong output โ
โ โ
โ ls "$FILENAME" โ
โ โโ shell sees: ls "My Document.txt" โ
โ โโ ONE argument: "My Document.txt" โ
โ โโ โ
correct โ
โ โ
โ Rule: quote every $VAR unless you have โ
โ a specific reason not to. โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Single vs Double Quotes
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SINGLE vs DOUBLE QUOTES โ
โ โ
โ NAME="Alice" โ
โ โ
โ echo 'Hello $NAME' โ
โ โโ prints: Hello $NAME โ
โ โโ literal, no expansion โ
โ โ
โ echo "Hello $NAME" โ
โ โโ prints: Hello Alice โ
โ โโ $ expands, quotes removed โ
โ โ
โ echo 'Today is $(date)' โ
โ โโ prints: Today is $(date) โ
โ โ
โ echo "Today is $(date)" โ
โ โโ prints: Today is 2026-05-03 โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| Assignment | NAME="value" โ no spaces around = |
| Reference | $NAME or ${NAME} |
| Single quotes | Suppress everything, fully literal |
| Double quotes | Allow $, `, \, " |
| Backslash | Escape next character only |
| Export | export VAR to pass to child processes |
| Word splitting | Suppressed by quoting |
| Glob expansion | Suppressed by quoting |
Unquoted $@ | Splits arguments at spaces |
Quoted "$@" | Preserves argument boundaries |
Key takeaways:
- Variables are assigned without spaces.
NAME="Alice"is correct.NAME = "Alice"is a command lookup that fails. The shell reads the value literally until the newline or semicolon, but spaces in the value require quotes . - Single quotes suppress everything. Inside
'...', no character has special meaning. Dollar signs are literal, asterisks are literal, backslashes are literal. You cannot embed a single quote without closing and reopening . - Double quotes allow expansion but prevent splitting. Inside
"...",$VARexpands,$(cmd)runs, and\escapes. But the result stays as one word โ spaces do not split it, and asterisks do not glob . - Always quote variable references. The rule of thumb is that
$VARand$(cmd)should almost always be inside double quotes. Unquoted expansion is the primary cause of scripts failing on filenames with spaces . - Braces disambiguate variable names.
${FILE}portis unambiguous.$FILEportlooks for a variable namedFILEport. - Export passes variables to child processes. Without
export, a variable set in a script is invisible to any command the script launches.exportconverts it into an environment variable . "$@"preserves argument boundaries. Unquoted$@splits each positional parameter at its internal spaces. Quoted"$@"forwards arguments exactly as they were received .
Remember: Variables hold text. Quoting controls what the shell does with that text. Single quotes say “take it literally.” Double quotes say “expand the variables, but keep it as one word.” No quotes say “split it, glob it, do whatever the shell would normally do.” The LFCA exam expects you to know which one to use and why. When in doubt, quote it โ and when you cannot quote it, know exactly why.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!