Bash Syntax and Variables

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:

  1. Read the concept — Understand the why

  2. Type the example — Don’t copy-paste; feel the syntax

  3. Modify it — Change variable names, values, see what breaks

  4. Predict output — Before running, guess what bash will do

  5. Trace execution — Use set -x to 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:

  1. Stores filenames in variables

  2. Uses variables in commands

  3. Captures command output

  4. Does arithmetic

  5. 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.