Variables and Quoting
Variables and Quoting#
Concepts#
Variables#
Variables store values (text or numbers) that you can use later.
Setting Variables#
name="Alice" # string (no spaces around `=` !)
age=30 # number (stored as string internally)
path="/var/log" # path
greeting="Hello, $name" # variable expansion inside double quotes
Critical rule: No spaces around =. This is wrong and will fail:
name = "Alice" # WRONG — bash interprets "name" as a command
Using Variables#
Prefix with $ or wrap in ${}:
echo $name # Alice
echo "$name is $age" # Alice is 30
echo "${name}_backup" # Alice_backup (braces clarify the boundary)
echo "$name_backup" # WRONG — looks for variable $name_backup
Use ${} when the variable name is adjacent to other text. It is always safe to use ${}.
Unsetting Variables#
unset name # remove the variable
echo $name # empty
Quoting#
Quoting controls how the shell interprets special characters. This is one of the most important (and confusing) topics in shell scripting.
Double Quotes "..." — Weak Quoting#
Variables and command substitution are expanded. Spaces are preserved.
name="Alice"
echo "Hello, $name" # Hello, Alice (variable expanded)
echo "Today is $(date)" # Today is Wed Oct 15... (command expanded)
echo "Files: $(ls)" # command substitution works
echo "Path: $HOME/docs" # variable expanded
Always double-quote variables to prevent word splitting and globbing:
file="my document.txt"
cat $file # WRONG — tries to open "my" and "document.txt"
cat "$file" # RIGHT — opens "my document.txt"
Single Quotes '...' — Strong Quoting#
Nothing is expanded. Everything is treated literally.
echo 'Hello, $name' # Hello, $name (literal $name)
echo 'Today is $(date)' # Today is $(date) (literal)
echo 'No expansion: \n $HOME' # No expansion: \n $HOME (literal)
Use single quotes when you want the exact text, no interpretation.
Backticks `...` — Command Substitution (Old Style)#
echo "Today is `date`" # same as $(date), but harder to nest
# Prefer $() as it nests: echo "$(echo "$(date)")"
No Quotes — Dangerous#
Without quotes, the shell performs word splitting and globbing:
files="*.txt"
echo $files # expands *.txt to actual filenames!
echo "$files" # prints: *.txt (literal)
message="hello world"
echo $message # hello world (extra spaces removed)
echo "$message" # hello world (preserved)
Rule of thumb: Always double-quote "$variables" unless you have a specific reason not to.
Environment Variables vs Local Variables#
-
Local variables exist only in the current shell/script:
myvar="local" -
Environment variables are exported and available to child processes:
export MY_ENV_VAR="exported"
# Set and export in one line
export DATABASE_URL="postgres://localhost:5432/mydb"
# Check if a variable is exported
env | grep MY_ENV_VAR
# Common environment variables
echo $HOME # /home/kmiguel
echo $USER # kmiguel
echo $SHELL # /bin/bash
echo $PATH # command search path
echo $PWD # current directory
echo $LANG # locale settings
echo $EDITOR # preferred text editor
echo $TERM # terminal type
Arithmetic#
Bash variables are strings. For arithmetic, use $(( )):
a=5
b=3
echo $((a + b)) # 8
echo $((a - b)) # 2
echo $((a * b)) # 15
echo $((a / b)) # 1 (integer division!)
echo $((a % b)) # 2 (remainder)
echo $((a ** 2)) # 25 (exponentiation)
# Increment
((a++))
echo $a # 6
# Assignment
result=$((a * b + 2))
echo $result
Note: Bash only does integer arithmetic. For floating-point, use bc:
echo "scale=2; 10 / 3" | bc # 3.33
echo "scale=4; 22 / 7" | bc # 3.1428
Parameter Expansion#
Bash provides powerful ways to manipulate variables:
name="Alice"
path="/home/alice/documents/report.txt"
# Default values
echo ${unset_var:-"default"} # prints "default" (var is not set)
echo ${unset_var:="default"} # sets var to "default" AND prints it
# String length
echo ${#name} # 5
# Substring
echo ${name:0:3} # Ali (from position 0, length 3)
echo ${name:2} # ice (from position 2 to end)
# Remove pattern from front
echo ${path#*/} # home/alice/documents/report.txt (shortest match)
echo ${path##*/} # report.txt (longest match — like basename)
# Remove pattern from back
echo ${path%/*} # /home/alice/documents (shortest match — like dirname)
echo ${path%%/*} # (empty — longest match)
# Substitution
echo ${name/Alice/Bob} # Bob (replace first occurrence)
echo ${path//./_} # all dots replaced with underscores
# Uppercase / lowercase (Bash 4+)
echo ${name^^} # ALICE
echo ${name,,} # alice
echo ${name^} # Alice (capitalize first letter)
Arrays#
# Create an array
fruits=("apple" "banana" "cherry" "date")
# Access elements (0-indexed)
echo ${fruits[0]} # apple
echo ${fruits[2]} # cherry
# All elements
echo ${fruits[@]} # apple banana cherry date
# Number of elements
echo ${#fruits[@]} # 4
# Add an element
fruits+=("elderberry")
# Loop over array
for fruit in "${fruits[@]}"; do
echo "Fruit: $fruit"
done
# Array indices
echo ${!fruits[@]} # 0 1 2 3 4
# Slice
echo ${fruits[@]:1:2} # banana cherry (from index 1, length 2)
Lab#
Exercise 1: Variables and Expansion#
cd ~/lab/scripts
cat > variables.sh << 'SCRIPT'
#!/bin/bash
# Setting variables
greeting="Hello"
name="World"
count=42
# Using variables
echo "$greeting, $name!"
echo "The count is: $count"
echo "${greeting}_${name}"
# Demonstrating the need for braces
item="file"
echo "Looking for ${item}s..."
echo "Looking for $items..." # WRONG — $items is undefined
# Environment variables
echo "Home: $HOME"
echo "User: $USER"
echo "Shell: $SHELL"
SCRIPT
chmod +x variables.sh
./variables.sh
Exercise 2: Quoting Differences#
cd ~/lab/scripts
cat > quoting.sh << 'SCRIPT'
#!/bin/bash
name="Alice"
path="/tmp/test dir/file.txt"
echo "=== Double Quotes ==="
echo "Hello, $name"
echo "Date: $(date +%Y-%m-%d)"
echo "Home: $HOME"
echo ""
echo "=== Single Quotes ==="
echo 'Hello, $name'
echo 'Date: $(date +%Y-%m-%d)'
echo 'Home: $HOME'
echo ""
echo "=== No Quotes (Dangerous) ==="
msg="hello world foo"
echo $msg # spaces collapsed
echo "$msg" # spaces preserved
echo ""
echo "=== Quoting Filenames ==="
echo "With quotes: '$path' (safe)"
# Without quotes, the space would split it into two arguments
SCRIPT
chmod +x quoting.sh
./quoting.sh
Exercise 3: Arithmetic#
cd ~/lab/scripts
cat > math.sh << 'SCRIPT'
#!/bin/bash
a=15
b=4
echo "a = $a, b = $b"
echo "a + b = $((a + b))"
echo "a - b = $((a - b))"
echo "a * b = $((a * b))"
echo "a / b = $((a / b)) (integer division)"
echo "a % b = $((a % b)) (remainder)"
echo "a^2 = $((a ** 2))"
# Increment
((a++))
echo "After a++: a = $a"
# Floating point with bc
echo "Precise division: $(echo "scale=2; 15 / 4" | bc)"
SCRIPT
chmod +x math.sh
./math.sh
Exercise 4: Parameter Expansion#
cd ~/lab/scripts
cat > expansion.sh << 'SCRIPT'
#!/bin/bash
filename="/home/user/documents/report_final.pdf"
echo "Full path: $filename"
echo "Filename: ${filename##*/}" # report_final.pdf
echo "Directory: ${filename%/*}" # /home/user/documents
echo "Extension: ${filename##*.}" # pdf
echo "No ext: ${filename%.*}" # /home/user/documents/report_final
# Default values
echo "DB_HOST is: ${DB_HOST:-localhost}" # localhost (not set)
echo "HOME is: ${HOME:-not set}" # /home/... (it IS set)
# String operations
text="Hello World"
echo "Length: ${#text}" # 11
echo "Upper: ${text^^}" # HELLO WORLD
echo "Lower: ${text,,}" # hello world
echo "Replace: ${text/World/Linux}" # Hello Linux
SCRIPT
chmod +x expansion.sh
./expansion.sh
Exercise 5: Arrays#
cd ~/lab/scripts
cat > arrays.sh << 'SCRIPT'
#!/bin/bash
# Define an array
colors=("red" "green" "blue" "yellow" "purple")
echo "First color: ${colors[0]}"
echo "Third color: ${colors[2]}"
echo "All colors: ${colors[@]}"
echo "Count: ${#colors[@]}"
# Add an element
colors+=("orange")
echo "After adding: ${colors[@]}"
# Loop
echo ""
echo "Listing colors:"
for color in "${colors[@]}"; do
echo " - $color"
done
# Build an array from command output
logfiles=($(ls /var/log/*.log 2>/dev/null))
echo ""
echo "Found ${#logfiles[@]} .log files in /var/log"
SCRIPT
chmod +x arrays.sh
./arrays.sh
Review#
1. Why must there be no spaces around `=` when setting a variable?
Bash interprets name = "value" as running a command called name with arguments = and "value". The assignment name="value" (no spaces) is the correct syntax.
2. What is the difference between single quotes and double quotes?
Double quotes "..." allow variable expansion ($var) and command substitution ($(cmd)). Single quotes '...' treat everything literally — no expansion occurs.
3. Why should you always double-quote variables?
Without quotes, the shell performs word splitting (spaces in values become separate arguments) and globbing (* is expanded to matching filenames). Quoting prevents these unintended behaviors.
4. How do you do arithmetic in Bash?
Use $(( )): result=$((5 + 3)). For floating-point, pipe to bc: echo "scale=2; 10/3" | bc.
5. What does `${var:-default}` do?
If $var is unset or empty, it expands to default. If $var has a value, it expands to that value. This provides a fallback without modifying the variable.
6. How do you extract the filename from a full path using parameter expansion?
${path##*/} — this removes the longest match of */ from the front, leaving just the filename. For example, /home/user/file.txt becomes file.txt.
7. What is the difference between `$@` and `$*`?
Both expand to all arguments. "$@" (quoted) preserves each argument as a separate word — this is almost always what you want. "$*" (quoted) joins all arguments into a single string separated by the first character of $IFS.
Previous: Your First Script | Next: Conditionals