5. Bash Syntax and Variables#
Up to now, you’ve used bash as a command-line interpreter. This chapter marks the beginning of Part II: Bash as a Programming Language. You’ll learn to write scripts that store data, make decisions, loop, and automate complex tasks.
This chapter focuses on the fundamentals: how bash interprets commands, how to create and use variables, and the different ways to reference data. These concepts underpin everything that follows.
What NOT to Do
These are common beginner mistakes. We’ll show you the right way:
# ❌ WRONG: Space around =
$ var = 5
bash: var: command not found
# ❌ WRONG: Variables don't auto-expand in single quotes
$ echo '$HOME'
$HOME
# ❌ WRONG: Quoting mistakes with special characters
$ grep $search logfile.txt
# If $search contains spaces, this breaks
# ✓ RIGHT: You'll learn this in this chapter
$ grep "$search" logfile.txt
Let’s begin.
Common Misconceptions to Avoid
❌ “Variables are automatically expanded” — They’re only expanded in certain contexts
❌ “Quotes don’t matter” — They control how bash parses the line
❌ “Spaces around = are optional” — They break variable assignment
❌ “$var is a variable reference, var is the value” — Both can mean different things
❌ “Arithmetic just works” — You need special syntax like $((expr))
This chapter will clarify all of these.
Learning Strategy
For this chapter:
Read the concept — Understand the why
Type the example — Don’t copy-paste; feel the syntax
Modify it — Change variable names, values, see what breaks
Predict output — Before running, guess what bash will do
Trace execution — Use
set -xto see what bash does
This hands-on approach builds intuition faster than passive reading.
A Quick Scripting Example
To motivate what’s coming, here’s a simple script using variables:
#!/bin/bash
# Variables store data
filename="data.csv"
output="report.txt"
# Use variables in commands
echo "Processing $filename..."
grep -E "ERROR|WARNING" "$filename" | wc -l > "$output"
# Use variables in arithmetic
lines=$(wc -l < "$output")
echo "Found $lines issues in $output"
# Conditionals (next chapter)
if [ $lines -gt 100 ]; then
echo "Warning: many issues detected!"
fi
In just 13 lines, this script:
Stores filenames in variables
Uses variables in commands
Captures command output
Does arithmetic
Makes a decision based on a value
And you could run it on any data files, any time. No manual intervention needed.
How Variables Work: The Big Picture
In bash, everything is a string. Variables store strings. When you use a variable, bash expands it (replaces $var with its value).
# Create a variable (no spaces around =)
$ name="Alice"
# Use the variable
$ echo "Hello, $name"
Hello, Alice
# Variables are strings
$ count=5
$ echo $count + 3 # Doesn't do math!
5 + 3
# To do math, use special syntax
$ echo $((count + 3))
8
This simple concept—strings stored in variables, expanded when referenced—is the foundation of bash programming.
The Scripting Journey
This chapter is Part 1 of your scripting education:
Part |
Focus |
Chapters |
|---|---|---|
Fundamentals |
Syntax, variables, operations |
5-6 (this part) |
Control Flow |
Decisions and loops |
7 |
Functions |
Modularity and reuse |
8 |
I/O & Text |
Reading/writing, processing |
9 |
Advanced |
Error handling, debugging |
11 |
Automation |
Jobs, cron, real systems |
12 |
Each chapter builds on the previous. You can’t write loops without understanding variables. You can’t write functions without understanding scope. Patience pays off.
What You'll Learn
By the end of this chapter:
✓ Understand how bash parses command lines
✓ Use single, double, and no quotes correctly
✓ Create and use variables effectively
✓ Apply variable expansion and substitution
✓ Perform arithmetic and string operations
✓ Debug script syntax errors
✓ Build reusable script templates
Chapter Overview
Section |
Topic |
Key Concepts |
|---|---|---|
0502 |
Command Syntax & Quoting |
How bash parses commands, quote types, escaping |
0503 |
Variables & Expansion |
Creating variables, $var syntax, word splitting |
0504 |
Parameter Substitution |
Advanced variable syntax, defaults, string ops |
0505 |
Arithmetic & Strings |
Math operations, string manipulation, concatenation |
0506 |
Lab: Variable Operations |
Hands-on exercises combining all concepts |
Prerequisites
You should be comfortable with:
Basic shell navigation: cd, ls, pwd
File operations: cat, grep, pipes
Permissions: chmod, running scripts
Text viewing: less, cat, head
If you’re rusty on these, review Chapters 1-4 first.
Why This Matters
Every task you do manually on the command line can be automated in a script:
Repetitive tasks: Run once, never type again
Complex workflows: Chain multiple steps reliably
Data processing: Handle thousands of files
System administration: Configure servers, manage backups, monitor systems
DevOps: Automate deployments and infrastructure
But before you can write powerful scripts, you need to understand how bash:
Parses commands: Quotes, escaping, word splitting
Stores data: Variables and their expansion
Performs operations: Arithmetic, string manipulation, comparisons
From Interactive to Scripting
There’s a big leap from typing commands interactively to writing scripts:
Interactive shell:
$ ls -la
$ grep "error" app.log
$ echo $HOME
Bash script:
#!/bin/bash
for file in *.log; do
echo "Processing $file..."
grep "error" "$file" >> errors.txt
done
The syntax is identical, but scripts add structure, reusability, and automation.
Real-World Scenario: Configuration Management
Imagine you’re deploying a web application to multiple environments (development, staging, production). Each environment has different settings:
Different database URLs
Different API keys
Different log levels
Different resource limits
Instead of maintaining multiple configuration files, you can use a single script with variables:
#!/bin/bash
# Configuration based on environment
ENVIRONMENT="${1:-.dev}"
case "$ENVIRONMENT" in
dev)
DB_HOST="localhost"
DB_PORT="5432"
DB_NAME="myapp_dev"
LOG_LEVEL="DEBUG"
API_KEY="dev-key-12345"
;;
staging)
DB_HOST="db-staging.internal"
DB_PORT="5432"
DB_NAME="myapp_staging"
LOG_LEVEL="INFO"
API_KEY="staging-key-67890"
;;
prod)
DB_HOST="db-prod.internal"
DB_PORT="5432"
DB_NAME="myapp_prod"
LOG_LEVEL="WARN"
API_KEY="${PROD_API_KEY}" # From secure environment
;;
esac
# Validate required variables are set
for var in DB_HOST DB_PORT DB_NAME LOG_LEVEL API_KEY; do
if [ -z "${!var}" ]; then
echo "ERROR: $var is not set"
exit 1
fi
done
# Use variables to configure the application
cat > app-config.json <<EOF
{
"database": {
"host": "$DB_HOST",
"port": $DB_PORT,
"name": "$DB_NAME"
},
"logging": {
"level": "$LOG_LEVEL"
},
"api": {
"key": "$API_KEY"
}
}
EOF
echo "Configuration created for $ENVIRONMENT environment"
This demonstrates:
Variable assignment based on conditions
Variable expansion in strings
Indirect variable references (${!var})
Here-documents for multi-line content
Configuration management as code
Sample Script: Backup Utility with Variables
A practical backup script that demonstrates all variable concepts:
#!/bin/bash
#===================================
# Backup Utility with Configuration
#===================================
# Configuration variables
readonly SCRIPT_NAME="$(basename "$0")"
readonly BACKUP_DIR="./backups"
readonly SOURCE_DIR="${1:-.}"
readonly BACKUP_NAME="backup-$(date +%Y%m%d-%H%M%S)"
readonly BACKUP_FILE="$BACKUP_DIR/$BACKUP_NAME.tar.gz"
# Counters for statistics
files_backed_up=0
total_size=0
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Error handling
set -euo pipefail
#-----------------------------------
# Functions using variables
#-----------------------------------
log_info() {
echo -e "${GREEN}[INFO]${NC} $*"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $*" >&2
}
log_error() {
echo -e "${RED}[ERROR]${NC} $*" >&2
}
# Validate input
validate_source() {
if [ ! -d "$SOURCE_DIR" ]; then
log_error "Source directory not found: $SOURCE_DIR"
return 1
fi
}
# Count files to backup
count_files() {
find "$SOURCE_DIR" -type f | wc -l
}
# Calculate directory size
calculate_size() {
du -sh "$SOURCE_DIR" | cut -f1
}
# Create backup directory
prepare_backup_dir() {
if [ ! -d "$BACKUP_DIR" ]; then
mkdir -p "$BACKUP_DIR"
log_info "Created backup directory: $BACKUP_DIR"
fi
}
# Create the backup
create_backup() {
log_info "Starting backup of $SOURCE_DIR..."
tar -czf "$BACKUP_FILE" -C "$(dirname "$SOURCE_DIR")" "$(basename "$SOURCE_DIR")"
local backup_size=$(du -h "$BACKUP_FILE" | cut -f1)
log_info "Backup created: $BACKUP_FILE (size: $backup_size)"
}
# Generate report
generate_report() {
local file_count=$(count_files)
local source_size=$(calculate_size)
local backup_size=$(du -h "$BACKUP_FILE" | cut -f1)
local compression_ratio=$((100 - (backup_size / source_size) * 100))
log_info "Backup Report:"
echo " Source: $SOURCE_DIR"
echo " Destination: $BACKUP_FILE"
echo " Files: $file_count"
echo " Source Size: $source_size"
echo " Backup Size: $backup_size"
echo " Compression: ~${compression_ratio}%"
echo " Timestamp: $(date)"
}
#-----------------------------------
# Main execution
#-----------------------------------
main() {
log_info "Starting $SCRIPT_NAME"
validate_source || exit 1
prepare_backup_dir
create_backup
generate_report
log_info "Backup completed successfully"
}
# Execute main function
main "$@"
What this script demonstrates:
readonly variables: Immutable configuration
Command substitution: \((command) and \)(date)
Variable expansion: Using variables in paths and strings
Parameter expansion: Default values (${1:-.})
String operations: Extracting parts of variables
Arithmetic: Simple calculations with variables
Conditional logic: Testing if variables are set
Functions: Organizing code with reusable variable scopes
Practical scripting: Real-world configuration and automation
This is the foundation you’ll build on throughout the rest of the course.