Lecture 1 / 12
Lecture 01 ยท Fundamentals

Introduction to Bash/Shell & Setup

Beginner ~45 min

What is Bash/Shell?

Bash (Bourne Again SHell) is the most popular Unix shell and command-line interpreter. It is the default shell on Linux and macOS and is essential for automation, DevOps, system administration, and scripting.

Why Learn Bash?

  • Automate repetitive tasks
  • Essential for DevOps, CI/CD, and cloud operations
  • Powerful text processing with sed, awk, grep
  • Works everywhere (Linux servers, macOS, WSL)

Setup & Installation

Bash is pre-installed on Linux and macOS. On Windows, you have several options:

  • WSL (Windows Subsystem for Linux) - Recommended for full Linux compatibility
  • Git Bash - Comes with Git for Windows
  • Cygwin - POSIX-compatible environment
  • Terminal (Windows 11) - Built-in support

Installing WSL on Windows

PowerShell (Admin)
wsl --install

Checking Your Bash Version

terminal
bash --version
echo $BASH_VERSION
echo $SHELL
Output
GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu) 5.1.16(1)-release /bin/bash

Essential First Commands

terminal
# System information
uname -a              # Kernel info
whoami                # Current user
pwd                   # Present working directory
date                  # Current date/time
echo $HOME            # Home directory
echo $PATH | tr ':' '\n'  # PATH directories

Creating Your First Script

hello.sh
#!/bin/bash
# My first Bash script

echo "========================================"
echo "  Welcome to Bash Scripting!"
echo "========================================"
echo "Date: $(date)"
echo "User: $(whoami)"
echo "Host: $(uname -n)"
echo "OS:   $(uname -o)"
echo "========================================"
terminal
# Make script executable and run
chmod +x hello.sh
./hello.sh
๐ŸŽฏ Exercise 1.1: System Info Script

Create a script called system_info.sh that displays:

  • Current date and time
  • Logged in user
  • Hostname
  • Current directory
  • Bash version
๐ŸŽฏ Exercise 1.2: Directory Navigator

Write a script that:

  • Creates a directory called bash_practice
  • Changes into it
  • Creates 3 subdirectories: scripts, logs, data
  • Lists the structure
Lecture 02 ยท Fundamentals

Variables & Data Types

Beginner ~55 min

Understanding Bash Variables

In Bash, there are no strict data types. Everything is essentially a string, but can be treated as an integer in arithmetic contexts. Variable names are case-sensitive and can contain letters, numbers, and underscores (cannot start with a number).

Variable Declaration Rules

variables.sh
# โœ“ CORRECT - No spaces around =
NAME="Alice"
AGE=25
_PRICE=99.99

# โœ— WRONG - Spaces cause errors
# NAME = "Alice"  # Error: command not found
# AGE = 25        # Error: command not found

# Accessing variables
echo "Hello $NAME, you are $AGE years old."
echo "Alternative syntax: ${NAME}"

Variable Types

variable_types.sh
# String variables
USER="john_doe"
QUOTE='Single quotes prevent $expansion'
MESSAGE="Double quotes allow $USER expansion"

# Integer (treated as string until arithmetic)
COUNT=42
YEAR=2024

# Readonly variables (constants)
readonly PI=3.14159
readonly APP_NAME="MyApplication"
# PI=3.14  # Error: readonly variable

# Unset variables
TEMP_VAR="temporary"
unset TEMP_VAR
# echo $TEMP_VAR  # Empty, no error

Special Variables

special_vars.sh
#!/bin/bash
echo "Script name: $0"
echo "First argument: $1"
echo "Second argument: $2"
echo "All arguments: $@"
echo "Argument count: $#"
echo "Process ID: $$"
echo "Last exit code: $?"

Command Substitution

command_sub.sh
# Modern syntax: $()
CURRENT_DATE=$(date +%Y-%m-%d)
FILES_COUNT=$(ls | wc -l)
USER_NAME=$(whoami)

# Legacy syntax: `` (backticks) - avoid
# OLD_WAY=`date +%Y`

echo "Today is: $CURRENT_DATE"
echo "Files in directory: $FILES_COUNT"

# Nested command substitution
FILES_WITH_EXT=$(ls $(pwd)/*.sh 2>/dev/null | wc -l)
echo "Shell scripts: $FILES_WITH_EXT"

Environment & Export

environment.sh
# Make variable available to child processes
export API_KEY="sk-1234567890abcdef"
export DB_HOST="localhost"
export DB_PORT=5432

# View all environment variables
env | grep "^API_"
printenv DB_HOST

# Add to PATH
export PATH="$HOME/bin:$PATH"

# Remove from environment
export -n API_KEY  # Remove export property
unset API_KEY      # Delete variable entirely
๐ŸŽฏ Exercise 2.1: User Profile Script

Create a script that stores and displays user information:

  • Store username, home directory, and shell in variables
  • Use command substitution to get current date/time
  • Display a formatted profile card
  • Export a custom variable called MY_APP
๐ŸŽฏ Exercise 2.2: Argument Processor

Create a script that accepts two arguments (first name and last name) and outputs:

  • Full name combining both arguments
  • Total number of arguments passed
  • Script name that was executed
  • Exit code of the last command
Lecture 03 ยท Fundamentals

Operators & Expressions

Beginner ~45 min

Arithmetic Operators

Bash supports integer arithmetic using multiple syntaxes:

Arithmetic Expansion $(( ))

arithmetic.sh
# Basic operations
RESULT=$(( 10 + 5 ))
echo "10 + 5 = $RESULT"

# All arithmetic operators
echo "Addition: $(( 20 + 10 ))"
echo "Subtraction: $(( 20 - 10 ))"
echo "Multiplication: $(( 20 * 10 ))"
echo "Division: $(( 20 / 10 ))"
echo "Modulo: $(( 20 % 7 ))"
echo "Exponent: $(( 2 ** 10 ))"

# Using variables
NUM=5
echo "NUM squared: $(( NUM * NUM ))"

let Command

let_demo.sh
# 'let' for arithmetic - no $ needed
let "a = 5 + 3"
let "b = a * 2"
let "x += 5"   # compound: x = x + 5
let "x *= 2"   # compound: x = x * 2

Comparison Operators

comparisons.sh
# Numeric comparisons
[ "$NUM1" -eq "$NUM2" ] # Equal
[ "$NUM1" -ne "$NUM2" ] # Not equal
[ "$NUM1" -lt "$NUM2" ] # Less than
[ "$NUM1" -le "$NUM2" ] # Less or equal
[ "$NUM1" -gt "$NUM2" ] # Greater than
[ "$NUM1" -ge "$NUM2" ] # Greater or equal

# String comparisons
[ "$STR1" = "$STR2" ]   # Equal
[ "$STR1" != "$STR2" ]  # Not equal
[ -z "$STR" ]        # Empty (zero length)
[ -n "$STR" ]        # Not empty

File Test Operators

file_tests.sh
# Existence and type
[ -e "file" ]  # Exists
[ -f "file" ]  # Regular file
[ -d "dir" ]   # Directory
[ -L "link" ] # Symbolic link
[ -p "pipe" ] # Named pipe

# Permissions
[ -r "file" ] # Readable
[ -w "file" ] # Writable
[ -x "file" ] # Executable
[ -s "file" ] # Non-empty

# File comparisons
[ "file1" -nt "file2" ] # file1 newer than file2
[ "file1" -ot "file2" ] # file1 older than file2

Logical Operators

logical.sh
# AND (&& / -a)
[ -f "file" ] && [ -r "file" ]
[ -f "file" -a -r "file" ]

# OR (|| / -o)
[ -f "file" ] || [ -d "file" ]

# NOT (!)
[ ! -f "file" ] # File does not exist
๐ŸŽฏ Exercise 3.1: Calculator Script

Create a script that accepts 3 arguments: two numbers and an operator (+, -, *, /), then performs the calculation and displays the result.

๐ŸŽฏ Exercise 3.2: File Inspector

Write a script that checks if a file (provided as argument) exists and reports:

  • File type (regular, directory, link, etc.)
  • Read/write/execute permissions
  • File size (empty or not)
Lecture 04 ยท Fundamentals

Control Flow: If & Case

Beginner ~65 min

If Statements

Bash supports if/elif/else chains for complex decision making:

Basic If Structure

if_basic.sh
#!/bin/bash

AGE=25

if [ "$AGE" -ge 18 ]; then
    echo "You are an adult"
else
    echo "You are a minor"
fi

# One-liner with && and ||
[ "$AGE" -ge 18 ] && echo "Adult" || echo "Minor"

If-Elif-Else Chains

if_elif.sh
#!/bin/bash

SCORE=85

if [ "$SCORE" -ge 90 ]; then
    echo "Grade: A"
    echo "Excellent work!"
elif [ "$SCORE" -ge 80 ]; then
    echo "Grade: B"
    echo "Good job!"
elif [ "$SCORE" -ge 70 ]; then
    echo "Grade: C"
    echo "Satisfactory"
elif [ "$SCORE" -ge 60 ]; then
    echo "Grade: D"
    echo "Needs improvement"
else
    echo "Grade: F"
    echo "Failed - study more"
fi

Nested If Statements

nested_if.sh
#!/bin/bash

USER_TYPE="admin"
LOGGED_IN="true"

if [ "$LOGGED_IN" = "true" ]; then
    echo "User is logged in"
    
    if [ "$USER_TYPE" = "admin" ]; then
        echo "Admin access granted"
        echo "Showing admin dashboard..."
    elif [ "$USER_TYPE" = "user" ]; then
        echo "User access granted"
        echo "Showing user dashboard..."
    else
        echo "Unknown user type"
    fi
else
    echo "Please log in first"
fi

Case Statements

Case is cleaner than multiple if/elif for matching patterns:

Basic Case

case_basic.sh
#!/bin/bash

DAY="Monday"

case "$DAY" in
    "Monday")
        echo "Start of work week"
        ;;
    "Friday")
        echo "Almost weekend!"
        ;;
    "Saturday"|"Sunday")
        echo "Weekend!"
        ;;
    *)
        echo "Regular work day"
        ;;
esac

Case with Command Arguments

case_menu.sh
#!/bin/bash
# Usage: ./script.sh [start|stop|restart|status]

COMMAND=$1

case "$COMMAND" in
    start|s)
        echo "Starting service..."
        # start commands here
        ;;
    stop|st)
        echo "Stopping service..."
        # stop commands here
        ;;
    restart|r)
        echo "Restarting service..."
        # restart commands here
        ;;
    status|stat)
        echo "Checking status..."
        # status commands here
        ;;
    help|-h|--help)
        echo "Usage: $0 {start|stop|restart|status}"
        ;;
    *)
        echo "Unknown command: $COMMAND"
        echo "Usage: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac

Pattern Matching in Case

case_patterns.sh
#!/bin/bash

FILENAME=$1

case "$FILENAME" in
    *.txt)
        echo "Text file"
        ;;
    *.sh)
        echo "Shell script"
        ;;
    *.jpg|*.jpeg|*.png|*.gif)
        echo "Image file"
        ;;
    *.tar.gz|*.tgz|*.zip)
        echo "Compressed archive"
        ;;
    [0-9]*)
        echo "Starts with a number"
        ;;
    *)
        echo "Unknown file type"
        ;;
esac
๐ŸŽฏ Exercise 4.1: Number Classifier

Write a script that takes a number and classifies it:

  • Positive/Negative/Zero
  • Even or Odd
  • Range: small (1-10), medium (11-100), large (>100)
๐ŸŽฏ Exercise 4.2: File Manager Menu

Create a case-based menu script with options:

  • List files in current directory
  • Show disk usage
  • Create a backup of specific file
  • Exit
Lecture 05 ยท Fundamentals

Loops

Beginner ~60 min

For Loops

Bash offers multiple ways to iterate - choose the right one for your use case:

C-style For Loop

for_cstyle.sh
# Traditional C-style loop (Bash 2.04+)
for (( i=0; i<5; i++ )); do
    echo "Iteration: $i"
done

# Countdown
for (( i=10; i>0; i-- )); do
    echo "Countdown: $i"
    sleep 1
done
echo "Blast off!"

# Multiple variables
for (( i=0, j=10; i<=10; i++, j-- )); do
    echo "i=$i, j=$j"
done

Range-based For Loop

for_range.sh
# Brace expansion {start..end}
for i in {1..5}; do
    echo "Number: $i"
done

# With step/increment (Bash 4.0+)
for i in {0..10..2}; do
    echo "Even number: $i"
done

# Reverse order
for i in {10..1}; do
    echo "Counting down: $i"
done

For Loop with Lists

for_list.sh
# Iterate over list of strings
for color in red green blue yellow; do
    echo "Color: $color"
done

# Iterate over array
FRUITS=("apple" "banana" "cherry")
for fruit in "${FRUITS[@]}"; do
    echo "Fruit: $fruit"
done

# Iterate over command output
for user in $(who | awk '{print $1}' | sort -u); do
    echo "Logged in user: $user"
done

File Globbing in For Loops

for_files.sh
# Process all text files
for file in *.txt; do
    [ -f "$file" ] || continue  # Skip if no matches
    echo "Processing: $file"
    wc -l "$file"
done

# Recursive processing
for file in /var/log/*.log; do
    [ -f "$file" ] || continue
    echo "$(basename "$file"): $(stat -c%s "$file") bytes"
done

# Multiple patterns
for file in *.sh *.bash; do
    [ -f "$file" ] || continue
    chmod +x "$file"
    echo "Made executable: $file"
done

While Loops

while_loops.sh
# Basic while loop
COUNTER=0
while [ "$COUNTER" -lt 5 ]; do
    echo "Counter: $COUNTER"
    ((COUNTER++))
done

# Reading file line by line
while IFS= read -r line; do
    echo "Line: $line"
done < file.txt

# Reading from command output
while read -r line; do
    echo "User: $line"
done < <(who)

# Infinite loop with break
while true; do
    read -p "Enter command (or 'quit'): " cmd
    [ "$cmd" = "quit" ] && break
    eval "$cmd"
done

Until Loops

until_loop.sh
# Until runs while condition is FALSE
# Perfect for "wait until something happens"

COUNT=10
until [ "$COUNT" -eq 0 ]; do
    echo "Count: $COUNT"
    ((COUNT--))
done

# Wait for file to exist
until [ -f "/tmp/ready.flag" ]; do
    echo "Waiting for file..."
    sleep 2
done
echo "File detected!"

Loop Control: break & continue

loop_control.sh
# break - exit loop completely
for i in {1..100}; do
    if [ "$i" -eq 50 ]; then
        echo "Found 50, stopping"
        break
    fi
done

# continue - skip to next iteration
for i in {1..20}; do
    if (( i % 2 == 0 )); then
        continue  # Skip even numbers
    fi
    echo "Odd: $i"
done

# break N - break out of N nested loops
for i in {1..3}; do
    for j in {a..c}; do
        echo "i=$i, j=$j"
        [ "$j" = "b" ] && break 2  # Break both loops
    done
done

Select Statement (Interactive Menus)

select_menu.sh
#!/bin/bash

PS3="Choose an option: "  # Prompt

select choice in "List Files" "Show Date" "Current User" "Exit"; do
    case "$choice" in
        "List Files")
            ls -la
            ;;
        "Show Date")
            date
            ;;
        "Current User")
            whoami
            ;;
        "Exit")
            echo "Goodbye!"
            break
            ;;
        *)
            echo "Invalid option"
            ;;
    esac
done
๐ŸŽฏ Exercise 5.1: Multiplication Table

Create a script that generates a multiplication table. The user should input a number, and the script prints the table from 1 to 10.

๐ŸŽฏ Exercise 5.2: Log File Analyzer

Write a script that:

  • Reads a log file line by line
  • Counts ERROR, WARNING, and INFO messages
  • Stops processing when it finds 5 ERROR entries
  • Prints a summary report
Lecture 06 ยท Core Concepts

Functions & Scripting

Intermediate ~75 min

Function Basics

Functions are reusable blocks of code that make scripts modular and easier to maintain.

Defining Functions

functions.sh
#!/bin/bash

# Style 1: function_name() { ... }
greet() {
    echo "Hello, World!"
}

# Style 2: function function_name { ... }
function say_goodbye {
    echo "Goodbye!"
}

# Call functions
greet
say_goodbye

Function Arguments

function_args.sh
#!/bin/bash

# Function with parameters
greet_user() {
    echo "Hello, $1!"      # $1 is first argument
    echo "You are $2 years old."  # $2 is second argument
    echo "Total arguments: $#"   # $# is argument count
    echo "All arguments: $@"      # $@ is all arguments
}

# Call with arguments
greet_user "Alice" 25
greet_user "Bob" 30 "extra"

Return Values

function_return.sh
#!/bin/bash

# Return exit codes (0-255)
check_file() {
    if [ -f "$1" ]; then
        return 0   # Success
    else
        return 1   # Failure
    fi
}

# Check return code with $?
check_file "existing.txt"
if [ $? -eq 0 ]; then
    echo "File exists!"
fi

# Return string output using echo (command substitution)
get_full_name() {
    echo "$1 $2"
}

FULL_NAME=$(get_full_name "John" "Doe")
echo "Full name: $FULL_NAME"

Variable Scope

scope.sh
#!/bin/bash

# Global variable (default)
GLOBAL_VAR="I am global"

# Function with local variables
demo_scope() {
    local LOCAL_VAR="I am local"
    GLOBAL_MOD="Modified in function"
    
    echo "Inside function:"
    echo "  LOCAL_VAR: $LOCAL_VAR"
    echo "  GLOBAL_VAR: $GLOBAL_VAR"
}

demo_scope

echo "Outside function:"
echo "  LOCAL_VAR: '$LOCAL_VAR' (empty - not accessible)"
echo "  GLOBAL_MOD: $GLOBAL_MOD"

Function Libraries

utils.sh
#!/bin/bash
# Library of utility functions

# Logging functions
log_info() {
    echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') - $@"
}

log_error() {
    echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $@" >&2
}

log_warn() {
    echo "[WARN] $(date '+%Y-%m-%d %H:%M:%S') - $@"
}

# File utilities
file_exists() {
    [ -f "$1" ]
}

dir_exists() {
    [ -d "$1" ]
}

# Validation functions
is_number() {
    [[ "$1" =~ ^-?[0-9]+$ ]]
}

is_email() {
    [[ "$1" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]
}

# Usage in main script:
# source ./utils.sh
# log_info "Starting process"

Recursive Functions

recursive.sh
#!/bin/bash

# Factorial calculation
factorial() {
    local n=$1
    if (( n <= 1 )); then
        echo 1
    else
        local prev=$(factorial $((n - 1)))
        echo $((n * prev))
    fi
}

echo "5! = $(factorial 5)"
echo "10! = $(factorial 10)"

# Directory tree listing (recursive)
list_tree() {
    local dir=$1
    local indent=$2
    
    for item in "$dir"/*; do
        [ -e "$item" ] || continue
        echo "${indent}$(basename "$item")"
        if [ -d "$item" ]; then
            list_tree "$item" "  $indent"
        fi
    done
}

Error Handling in Functions

error_handling.sh
#!/bin/bash

# Safe division with error checking
safe_divide() {
    local dividend=$1
    local divisor=$2
    
    # Validate inputs
    if ! [[ "$dividend" =~ ^-?[0-9]+$ ]]; then
        echo "Error: First argument must be a number" >&2
        return 1
    fi
    
    if ! [[ "$divisor" =~ ^-?[0-9]+$ ]]; then
        echo "Error: Second argument must be a number" >&2
        return 1
    fi
    
    if [ "$divisor" -eq 0 ]; then
        echo "Error: Cannot divide by zero" >&2
        return 1
    fi
    
    echo $((dividend / divisor))
    return 0
}

# Usage
result=$(safe_divide 100 5)
if [ $? -eq 0 ]; then
    echo "Result: $result"
else
    echo "Operation failed"
fi
๐ŸŽฏ Exercise 6.1: Math Library

Create a library file with functions for:

  • add(), subtract(), multiply(), divide()
  • is_even(), is_prime()
  • max(), min() (variable arguments)
๐ŸŽฏ Exercise 6.2: Backup Utility

Write a script with functions that:

  • create_backup() - backs up a file with timestamp
  • restore_backup() - restores from backup
  • list_backups() - shows available backups
  • cleanup_old_backups() - removes backups older than 7 days
Lecture 07 ยท Core Concepts

Arrays & String Manipulation

Intermediate ~70 min

Indexed Arrays

arrays.sh
# Declare arrays
FRUITS=("Apple" "Banana" "Cherry" "Date")
NUMBERS=(10 20 30 40 50)

# Access elements (0-indexed)
echo "${FRUITS[0]}"     # Apple (first element)
echo "${FRUITS[2]}"     # Cherry (third element)
echo "${FRUITS[-1]}"    # Date (last element - Bash 4.2+)
echo "${FRUITS[-2]}"    # Cherry (second to last)

# Array length
echo "Total fruits: ${#FRUITS[@]}"
echo "Also: ${#FRUITS[*]}"

# All elements
echo "All: ${FRUITS[@]}"
echo "Quoted: ${FRUITS[*]}"

Array Operations

array_ops.sh
# Add elements
FRUITS+=("Elderberry")    # Append single
FRUITS+=("Fig" "Grape")   # Append multiple

# Insert at specific index
FRUITS[1]="Blueberry"      # Replace index 1

# Remove elements
unset FRUITS[2]           # Remove element at index 2

# Slice array (Bash 4.0+)
echo "${FRUITS[@]:1:3}"    # 3 elements starting from index 1

# Iterate over array
for fruit in "${FRUITS[@]}"; do
    echo "Fruit: $fruit"
done

# Iterate with index
for i in "${!FRUITS[@]}"; do
    echo "Index $i: ${FRUITS[$i]}"
done

Associative Arrays (Bash 4.0+)

assoc_arrays.sh
# Declare associative array
declare -A USER_INFO

# Set values
USER_INFO[name]="Alice"
USER_INFO[age]=30
USER_INFO[city]="New York"

# Alternative declaration
declare -A COLORS=(
    [red]="#FF0000"
    [green]="#00FF00"
    [blue]="#0000FF"
)

# Access
echo "Name: ${USER_INFO[name]}"
echo "Hex for red: ${COLORS[red]}"

# All keys
echo "Keys: ${!USER_INFO[@]}"

# All values
echo "Values: ${USER_INFO[@]}"

# Iterate
for key in "${!USER_INFO[@]}"; do
    echo "$key -> ${USER_INFO[$key]}"
done

# Check if key exists
if [[ -v USER_INFO[name] ]]; then
    echo "Name key exists"
fi

String Manipulation

String Length & Extraction

string_ops.sh
STR="Hello, World!"

# Length
echo "${#STR}"           # 13

# Extract substring
echo "${STR:0:5}"       # Hello (start at 0, length 5)
echo "${STR:7}"         # World! (start at 7 to end)
echo "${STR: -6}"        # World! (last 6 chars - note space)

# Remove from start/end
echo "${STR#Hello, }"   # World! (remove shortest match from start)
echo "${STR% World!}"   # Hello (remove shortest match from end)

# Remove all occurrences
STR2="aaabbbcccaaa"
echo "${STR2//a/}"      # bbbccc (remove all 'a')
echo "${STR2//a/X}"     # XXXbbbcccXXX (replace all 'a' with 'X')

# Replace first occurrence only
echo "${STR2/a/X}"      # Xaabbbcccaaa

Case Conversion

case_convert.sh
TEXT="Hello World"

# Uppercase
echo "${TEXT^^}"        # HELLO WORLD (Bash 4.0+)
echo "${TEXT^^[hw]}"    # HEllo World (only h,w to uppercase)

# Lowercase
echo "${TEXT,,}"        # hello world

# Toggle case
echo "${TEXT~~}"        # hELLO wORLD

# Capitalize first character
echo "${TEXT^}"         # Hello world (first char only)

# Using tr command
echo "$TEXT" | tr '[:lower:]' '[:upper:]'
echo "$TEXT" | tr '[:upper:]' '[:lower:]'

Path Manipulation

path_ops.sh
FILE="/home/user/documents/report.txt"

# Get filename
echo "${FILE##*/}"      # report.txt

# Get directory
echo "${FILE%/*}"       # /home/user/documents

# Get extension
echo "${FILE##*.}"      # txt

# Remove extension
echo "${FILE%.*}"       # /home/user/documents/report

# Default value if empty
UNSET_VAR=""
echo "${UNSET_VAR:-default}"  # default
echo "${UNSET_VAR:=default}"  # Sets AND returns default

# Error if empty
# echo "${UNSET_VAR:?Variable is required}"
๐ŸŽฏ Exercise 7.1: Phone Book

Create an associative array-based phone book script with functions to:

  • Add contact (name โ†’ phone number)
  • Lookup contact by name
  • Delete contact
  • List all contacts
๐ŸŽฏ Exercise 7.2: Log Parser

Write a script that processes web server logs:

  • Extract IP addresses, timestamps, and URLs
  • Count requests per IP using associative array
  • Find most requested URLs
Lecture 08 ยท Core Concepts

File Operations & I/O

Intermediate ~75 min

Standard Streams

streams.sh
# In Linux, everything is a file including I/O streams:
# stdin  (0) - Standard Input  (keyboard by default)
# stdout (1) - Standard Output (screen by default)
# stderr (2) - Standard Error  (screen by default)

# Redirect stdout to file
echo "Hello" > output.txt      # Overwrite
echo "World" >> output.txt     # Append

# Redirect stderr
ls nonexistent 2> errors.log

# Redirect both stdout and stderr
command > output.log 2>&1
command &> output.log        # Bash 4+ shorthand

# Redirect to /dev/null (discard)
command > /dev/null 2>&1     # Silent execution

Advanced Redirects

advanced_io.sh
# Separate stdout and stderr to different files
command > output.log 2> errors.log

# Redirect stderr to stdout
command 2>&1

# Redirect stdout to stderr
echo "Error message" >&2

# Here documents (multi-line input)
cat << EOF
This is a multi-line
message that ends
with the delimiter EOF
EOF

# Here strings (single line input)
cat <<< "This is a single line"

# Process substitution (treat command output as file)
diff <(sort file1.txt) <(sort file2.txt)

Pipes

pipes.sh
# Basic pipe: command1 | command2
cat file.txt | grep "error" | wc -l

# Multiple pipes (pipeline)
ps aux | grep "apache" | awk '{print $2}' | xargs kill

# Pipe to while loop
cat file.txt | while read line; do
    echo "Processing: $line"
done

# Pipe with tee (split output)
command | tee output.log    # Show on screen AND save to file
command | tee -a output.log # Append instead of overwrite

# Named pipes (FIFO)
mkfifo mypipe
# Terminal 1: echo "data" > mypipe
# Terminal 2: cat < mypipe

File Operations

file_ops.sh
# Create files
touch newfile.txt           # Empty file
echo "content" > file.txt    # Create with content

# Copy files
cp source.txt dest.txt
cp -r dir1 dir2             # Recursive copy
cp -v file1 file2 dir/      # Verbose

# Move/Rename files
mv old.txt new.txt
mv file.txt /path/to/dest/

# Delete files
rm file.txt
rm -i file.txt             # Interactive (prompt)
rm -f file.txt             # Force (no error if missing)
rm -r directory/           # Recursive delete

# Create directories
mkdir newdir
mkdir -p path/to/nested/dir # Create parent directories

Reading Files

read_files.sh
# Read entire file
content=$(< file.txt)

# Read line by line
while IFS= read -r line; do
    echo "Line: $line"
done < file.txt

# Read with line numbers
n=1
while IFS= read -r line; do
    printf "%5d: %s\n" $n "$line"
    ((n++))
done < file.txt

# Read first N lines
head -n 10 file.txt

# Read last N lines
tail -n 10 file.txt

# Read specific line (line 5)
sed -n '5p' file.txt
awk 'NR==5' file.txt

Text Processing Tools

text_tools.sh
# sort - sort lines
sort file.txt              # Alphabetical
sort -n numbers.txt        # Numerical
sort -r file.txt           # Reverse
sort -k 2 file.txt         # Sort by 2nd column

# uniq - unique lines (input must be sorted)
uniq file.txt              # Remove duplicates
uniq -c file.txt           # Count occurrences
uniq -d file.txt           # Show only duplicates

# cut - extract columns
cut -d',' -f 1,3 file.csv  # CSV, fields 1 and 3# Characters 1-10

# wc - word count
wc -l file.txt             # Lines
wc -w file.txt             # Words
wc -c file.txt             # Bytes

# tr - translate characters
cat file.txt | tr 'a-z' 'A-Z'  # Uppercase
cat file.txt | tr -d '0-9'     # Delete digits
cat file.txt | tr -s ' '       # Squeeze spaces
๐ŸŽฏ Exercise 8.1: Log Analyzer

Write a script that analyzes system logs:

  • Count errors vs warnings vs info messages
  • Find most common error messages
  • Generate hourly activity report
  • Save report to file with timestamp
๐ŸŽฏ Exercise 8.2: File Synchronizer

Create a backup script that:

  • Copies files from source to destination
  • Only copies files that have changed (compare timestamps)
  • Creates a log of all operations
  • Reports total bytes transferred
Lecture 09 ยท Advanced

Process Management & Signals

Advanced ~70 min

Process Management

processes.sh
# List all processes
ps aux                    # BSD style (detailed)
ps -ef                    # UNIX style
ps aux | grep "nginx"   # Filter for specific process

# Get current shell PID
echo "My PID: $$"

# Get parent PID
echo "Parent PID: $PPID"

# Find process by name
pidof nginx              # Get PID of nginx
pgrep nginx              # List all nginx PIDs
pgrep -f "python script.py"  # Match full command line

# Check if process is running
if pgrep -x "sshd" > /dev/null; then
    echo "SSH service is running"
fi

Killing & Signaling Processes

signals.sh
# Common signals
# 1  (SIGHUP)   - Hang up (reload config)
# 2  (SIGINT)   - Interrupt (Ctrl+C)
# 9  (SIGKILL)  - Kill immediately (cannot be caught)
# 15 (SIGTERM)  - Terminate gracefully (default)
# 18 (SIGCONT)  - Continue (resume)
# 19 (SIGSTOP)  - Stop (pause, Ctrl+Z)

# Kill by PID
kill 1234                # Send SIGTERM (15) to PID 1234
kill -9 1234             # Force kill (SIGKILL)
kill -15 1234            # Graceful termination

# Kill by name
killall firefox          # Kill all firefox processes
pkill nginx              # Kill by pattern

# Send custom signal
kill -HUP 1234           # Reload config (SIGHUP)

Background & Foreground Jobs

jobs.sh
# Run command in background
sleep 60 &              # & runs in background
# Output: [1] 12345    # Job number [1], PID 12345

# List jobs
jobs                    # Show all background jobs
jobs -l                 # Include PIDs
jobs -r                 # Running jobs only
jobs -s                 # Stopped jobs only

# Bring job to foreground
fg %1                   # Bring job [1] to foreground
fg                      # Most recent job

# Send to background
Ctrl+Z                  # Suspend current job
bg %1                   # Resume job [1] in background

# Wait for background job
wait %1                 # Wait for job [1] to complete
wait                    # Wait for all background jobs

# Disown (prevent SIGHUP on logout)
disown %1               # Job survives logout
nohup script.sh &       # Run immune to hangups

Signal Handling in Scripts

trap.sh
#!/bin/bash
# trap - handle signals gracefully

# Cleanup function
cleanup() {
    echo "Cleaning up..."
    rm -f /tmp/tempfile_$$
    echo "Goodbye!"
    exit 0
}

# Set trap
trap cleanup EXIT       # Run on script exit
trap cleanup SIGINT     # Run on Ctrl+C
trap '' SIGTERM        # Ignore SIGTERM

# Reset trap
trap - EXIT             # Remove EXIT trap

# Example: temp file handler
TEMP_FILE=$(mktemp)
trap "rm -f $TEMP_FILE; exit 1" EXIT INT TERM

# Script continues here...
echo "Using temp file: $TEMP_FILE"
# ... do work ...

# Explicit cleanup (trap also runs on exit)
rm -f "$TEMP_FILE"
๐ŸŽฏ Exercise 9.1: Process Monitor

Write a script that:

  • Monitors if a process (by name) is running
  • Restarts it if it dies
  • Logs restarts with timestamp
  • Handles SIGTERM gracefully
๐ŸŽฏ Exercise 9.2: Parallel Task Runner

Create a script that:

  • Runs multiple commands in parallel (background)
  • Limits concurrent jobs to N at a time
  • Collects exit codes
  • Reports which succeeded/failed
Lecture 10 ยท Advanced

RegEx & grep

Advanced ~65 min

Powerful Text Processing

grep -E "^[0-9]+" data.txt # Find lines starting with numbers
sed -i 's/old/new/g' file.txt # Replace in file
awk '{print $1}' log.txt # Print first column
Lecture 11 ยท Advanced

DevOps & Automation

Advanced ~50 min

Cron Jobs

Schedule scripts to run automatically.

# Edit crontab
crontab -e
# Run every day at midnight
0 0 * * * /path/to/backup.sh
Lecture 12 ยท Capstone

Capstone Project: System Monitor

Advanced ~120 min

Create a script that monitors disk usage, CPU, and memory, and sends an alert if limits are exceeded.

# Project requirements:
# 1. Use df, top, and free commands
// 2. Implement logic to check thresholds
// 3. Write results to a log file
// 4. (Optional) Send an email/webhook notification