google-site-verification: google61fe8ba583a51912.html
Complete Bash Exercises with Solutions

Introduction

This comprehensive guide contains 100+ Bash exercises ranging from beginner to advanced levels. Each exercise includes a problem statement, hints, and a complete solution. These exercises cover all major Bash concepts including variables, loops, conditionals, functions, file operations, text processing, and more.

Table of Contents

  1. Basic Exercises
  2. Variables and Data Types
  3. Conditional Statements
  4. Loop Exercises
  5. Function Exercises
  6. File Operations
  7. Text Processing
  8. Arrays and Data Structures
  9. Advanced Scripting
  10. System Administration
  11. Real-World Projects

1. Basic Exercises

Exercise 1.1: Hello World

Problem: Write a script that prints "Hello, World!" to the console.

Hint: Use the echo command.

Solution:

#!/bin/bash echo "Hello, World!"

Exercise 1.2: User Input

Problem: Write a script that asks the user for their name and greets them.

Hint: Use read to get user input.

Solution:

#!/bin/bash echo "What is your name?" read name echo "Hello, $name! Nice to meet you."

Exercise 1.3: Command Line Arguments

Problem: Write a script that accepts two command-line arguments and prints them.

Hint: Use $1 and $2 for the first two arguments.

Solution:

#!/bin/bash echo "First argument: $1" echo "Second argument: $2" echo "Total arguments: $#" echo "All arguments: $@"

Exercise 1.4: Simple Calculator

Problem: Write a script that takes two numbers as input and prints their sum.

Hint: Use arithmetic expansion $((...)).

Solution:

#!/bin/bash echo "Enter first number:" read num1 echo "Enter second number:" read num2 sum=$((num1 + num2)) echo "Sum: $sum"

Exercise 1.5: Current Date and Time

Problem: Write a script that displays the current date and time in a readable format.

Hint: Use the date command.

Solution:

#!/bin/bash echo "Current date and time:" date echo "Formatted: $(date '+%Y-%m-%d %H:%M:%S')"

2. Variables and Data Types

Exercise 2.1: String Concatenation

Problem: Write a script that concatenates two strings entered by the user.

Hint: Simply place variables next to each other.

Solution:

#!/bin/bash echo "Enter first string:" read str1 echo "Enter second string:" read str2 result="$str1$str2" echo "Concatenated: $result"

Exercise 2.2: String Length

Problem: Write a script that calculates and prints the length of a string entered by the user.

Hint: Use ${#variable} syntax.

Solution:

#!/bin/bash echo "Enter a string:" read str length=${#str} echo "The string '$str' has $length characters."

Exercise 2.3: Substring Extraction

Problem: Extract and print the first 5 characters of a string, then characters from position 3 to 7.

Hint: Use ${variable:position:length}.

Solution:

#!/bin/bash echo "Enter a string (at least 10 characters):" read str first5="${str:0:5}" mid="${str:2:5}" echo "First 5 characters: $first5" echo "Characters 3-7: $mid"

Exercise 2.4: Uppercase/Lowercase Conversion

Problem: Convert a string to uppercase and lowercase.

Hint: Use tr command or Bash 4+ ${var^^} and ${var,,}.

Solution:

#!/bin/bash echo "Enter a string:" read str # Method 1: Using tr echo "Uppercase (tr): $(echo "$str" | tr '[:lower:]' '[:upper:]')" echo "Lowercase (tr): $(echo "$str" | tr '[:upper:]' '[:lower:]')" # Method 2: Bash 4+ built-in echo "Uppercase (bash): ${str^^}" echo "Lowercase (bash): ${str,,}"

Exercise 2.5: Variable Default Values

Problem: Write a script that uses a variable with a default value if not set.

Hint: Use ${var:-default} syntax.

Solution:

#!/bin/bash # Set a variable (comment out to test default) # name="John" echo "Hello, ${name:-World}!" # Assign default if not set message="${1:-Default message}" echo "Message: $message"

Exercise 2.6: Arithmetic Operations

Problem: Create a script that performs addition, subtraction, multiplication, and division on two numbers.

Hint: Use $((...)) for arithmetic.

Solution:

#!/bin/bash echo "Enter first number:" read a echo "Enter second number:" read b echo "Addition: $((a + b))" echo "Subtraction: $((a - b))" echo "Multiplication: $((a * b))" echo "Division: $((a / b))" echo "Modulus: $((a % b))" echo "Power: $((a ** 2)), $((b ** 2))"

Exercise 2.7: Floating Point Arithmetic

Problem: Perform floating-point division with 2 decimal places.

Hint: Use bc or awk for floating-point math.

Solution:

#!/bin/bash echo "Enter first number:" read a echo "Enter second number:" read b # Using bc result=$(echo "scale=2; $a / $b" | bc) echo "Division (bc): $result" # Using awk result=$(awk "BEGIN {printf \"%.2f\", $a/$b}") echo "Division (awk): $result"

3. Conditional Statements

Exercise 3.1: Even or Odd

Problem: Write a script that checks if a number is even or odd.

Hint: Use modulo operator % with if statement.

Solution:

#!/bin/bash echo "Enter a number:" read num if [ $((num % 2)) -eq 0 ]; then echo "$num is even" else echo "$num is odd" fi

Exercise 3.2: Number Comparison

Problem: Compare two numbers and print the larger one.

Hint: Use -gt, -lt, or -eq operators.

Solution:

#!/bin/bash echo "Enter first number:" read a echo "Enter second number:" read b if [ $a -gt $b ]; then echo "$a is greater than $b" elif [ $a -lt $b ]; then echo "$a is less than $b" else echo "$a equals $b" fi

Exercise 3.3: File Type Check

Problem: Check if a given path is a file, directory, or doesn't exist.

Hint: Use -f, -d, and -e test operators.

Solution:

#!/bin/bash echo "Enter a path:" read path if [ -e "$path" ]; then if [ -f "$path" ]; then echo "$path is a regular file" elif [ -d "$path" ]; then echo "$path is a directory" else echo "$path is some other type of file" fi else echo "$path does not exist" fi

Exercise 3.4: Grade Calculator

Problem: Convert a numerical score (0-100) to a letter grade (A, B, C, D, F).

Hint: Use elif chain for multiple conditions.

Solution:

#!/bin/bash echo "Enter your score (0-100):" read score if [ $score -ge 90 ]; then grade="A" elif [ $score -ge 80 ]; then grade="B" elif [ $score -ge 70 ]; then grade="C" elif [ $score -ge 60 ]; then grade="D" else grade="F" fi echo "Your grade is: $grade"

Exercise 3.5: Leap Year Check

Problem: Determine if a given year is a leap year.

Hint: Leap year rules: divisible by 4, but not by 100 unless also by 400.

Solution:

#!/bin/bash echo "Enter a year:" read year if [ $((year % 400)) -eq 0 ]; then echo "$year is a leap year" elif [ $((year % 100)) -eq 0 ]; then echo "$year is not a leap year" elif [ $((year % 4)) -eq 0 ]; then echo "$year is a leap year" else echo "$year is not a leap year" fi

Exercise 3.6: Case Statement Menu

Problem: Create a simple menu using case statement.

Hint: Use case $variable in pattern) commands;; esac.

Solution:

#!/bin/bash echo "Select an option:" echo "1) Display date" echo "2) Display calendar" echo "3) List files" echo "4) Current directory" echo "5) Exit" read choice case $choice in 1) date ;; 2) cal ;; 3) ls -la ;; 4) pwd ;; 5) echo "Goodbye!" exit 0 ;; *) echo "Invalid option" ;; esac

Exercise 3.7: Password Strength Checker

Problem: Check if a password meets criteria: length >= 8, contains uppercase, lowercase, and digit.

Hint: Use pattern matching and conditional operators.

Solution:

#!/bin/bash echo "Enter a password:" read -s password # Check length if [ ${#password} -lt 8 ]; then echo "Password too short (min 8 characters)" exit 1 fi # Check for uppercase if ! [[ "$password" =~ [A-Z] ]]; then echo "Password must contain at least one uppercase letter" exit 1 fi # Check for lowercase if ! [[ "$password" =~ [a-z] ]]; then echo "Password must contain at least one lowercase letter" exit 1 fi # Check for digit if ! [[ "$password" =~ [0-9] ]]; then echo "Password must contain at least one digit" exit 1 fi echo "Password is strong!"

4. Loop Exercises

Exercise 4.1: Print Numbers 1 to 10

Problem: Print numbers from 1 to 10 using a loop.

Hint: Use for loop with {1..10}.

Solution:

#!/bin/bash echo "Using for loop with range:" for i in {1..10}; do echo -n "$i " done echo echo "Using C-style for loop:" for ((i=1; i<=10; i++)); do echo -n "$i " done echo

Exercise 4.2: Sum of First N Numbers

Problem: Calculate the sum of first N natural numbers.

Hint: Use a loop to accumulate sum.

Solution:

#!/bin/bash echo "Enter a number:" read n sum=0 for ((i=1; i<=n; i++)); do sum=$((sum + i)) done echo "Sum of first $n numbers: $sum" # Formula method (more efficient) formula_sum=$((n * (n + 1) / 2)) echo "Formula verification: $formula_sum"

Exercise 4.3: Multiplication Table

Problem: Print the multiplication table for a given number.

Hint: Loop from 1 to 10 and multiply.

Solution:

#!/bin/bash echo "Enter a number:" read num echo "Multiplication table for $num:" for i in {1..10}; do result=$((num * i)) echo "$num × $i = $result" done

Exercise 4.4: Factorial Calculation

Problem: Calculate the factorial of a number.

Hint: Multiply numbers from 1 to n.

Solution:

#!/bin/bash echo "Enter a number:" read n factorial=1 for ((i=1; i<=n; i++)); do factorial=$((factorial * i)) done echo "$n! = $factorial"

Exercise 4.5: Fibonacci Sequence

Problem: Generate the first N numbers in the Fibonacci sequence.

Hint: Each number is the sum of the two preceding ones.

Solution:

#!/bin/bash echo "How many Fibonacci numbers to generate?" read n a=0 b=1 echo "Fibonacci sequence:" for ((i=0; i<n; i++)); do echo -n "$a " fn=$((a + b)) a=$b b=$fn done echo

Exercise 4.6: Prime Number Check

Problem: Check if a number is prime.

Hint: Check divisibility from 2 to sqrt(n).

Solution:

#!/bin/bash echo "Enter a number:" read num if [ $num -lt 2 ]; then echo "$num is not prime" exit 0 fi is_prime=1 for ((i=2; i*i<=num; i++)); do if [ $((num % i)) -eq 0 ]; then is_prime=0 break fi done if [ $is_prime -eq 1 ]; then echo "$num is prime" else echo "$num is not prime" fi

Exercise 4.7: Print Pattern - Triangle

Problem: Print a right-angled triangle of stars.

Hint: Use nested loops.

Solution:

#!/bin/bash echo "Enter number of rows:" read rows for ((i=1; i<=rows; i++)); do for ((j=1; j<=i; j++)); do echo -n "* " done echo done # Inverted triangle echo -e "\nInverted triangle:" for ((i=rows; i>=1; i--)); do for ((j=1; j<=i; j++)); do echo -n "* " done echo done

Exercise 4.8: Pyramid Pattern

Problem: Print a centered pyramid of stars.

Hint: Calculate spaces and stars for each row.

Solution:

#!/bin/bash echo "Enter number of rows:" read rows for ((i=1; i<=rows; i++)); do # Print spaces for ((j=i; j<rows; j++)); do echo -n " " done # Print stars for ((j=1; j<=i; j++)); do echo -n "* " done echo done

Exercise 4.9: Read File Line by Line

Problem: Read a file line by line and display line numbers.

Hint: Use while read loop.

Solution:

#!/bin/bash echo "Enter filename:" read filename if [ ! -f "$filename" ]; then echo "File not found!" exit 1 fi line_num=1 while IFS= read -r line; do echo "$line_num: $line" ((line_num++)) done < "$filename"

Exercise 4.10: Process All Files in Directory

Problem: List all files in a directory with their sizes.

Hint: Loop through files with for file in *.

Solution:

#!/bin/bash echo "Files in current directory:" echo "============================" for file in *; do if [ -f "$file" ]; then size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null) echo "📄 $file - ${size} bytes" elif [ -d "$file" ]; then echo "📁 $file - (directory)" fi done

Exercise 4.11: Guess the Number Game

Problem: Create a number guessing game where the computer picks a random number.

Hint: Use $RANDOM and loop until correct.

Solution:

#!/bin/bash secret=$((RANDOM % 100 + 1)) attempts=0 echo "I'm thinking of a number between 1 and 100." echo "Can you guess it?" while true; do echo -n "Your guess: " read guess ((attempts++)) if [ $guess -lt $secret ]; then echo "Too low!" elif [ $guess -gt $secret ]; then echo "Too high!" else echo "Correct! You guessed it in $attempts attempts." break fi done

Exercise 4.12: Countdown Timer

Problem: Create a countdown timer from a given number.

Hint: Use while loop with sleep.

Solution:

#!/bin/bash echo "Enter countdown seconds:" read seconds while [ $seconds -gt 0 ]; do echo -ne "Time remaining: $seconds seconds\033[0K\r" sleep 1 ((seconds--)) done echo -e "\nTime's up!"

Exercise 4.13: Menu with Select Loop

Problem: Create an interactive menu using the select loop.

Hint: Use select for menu generation.

Solution:

#!/bin/bash PS3="Choose an option: " options=("Show Date" "Show Calendar" "List Files" "Show Disk Usage" "Exit") select opt in "${options[@]}"; do case $opt in "Show Date") date ;; "Show Calendar") cal ;; "List Files") ls -la ;; "Show Disk Usage") df -h ;; "Exit") echo "Goodbye!" break ;; *) echo "Invalid option $REPLY" ;; esac echo -e "\nPress Enter to continue..." read done

Exercise 4.14: Infinite Loop with Break Condition

Problem: Create an infinite loop that breaks when the user types "quit".

Hint: Use while true and break.

Solution:

#!/bin/bash while true; do echo -n "Enter command (quit to exit): " read cmd if [ "$cmd" = "quit" ]; then echo "Exiting..." break fi echo "You entered: $cmd" eval "$cmd" done

5. Function Exercises

Exercise 5.1: Simple Function

Problem: Create a function that prints "Hello, World!"

Hint: Define function with function_name() { ... }.

Solution:

#!/bin/bash greet() { echo "Hello, World!" } # Call the function greet

Exercise 5.2: Function with Parameters

Problem: Create a function that greets a person by name.

Hint: Access parameters with $1, $2, etc.

Solution:

#!/bin/bash greet_person() { echo "Hello, $1! How are you today?" } echo "Enter your name:" read name greet_person "$name"

Exercise 5.3: Function with Return Value

Problem: Create a function that adds two numbers and returns the result.

Hint: Use echo to return value, capture with command substitution.

Solution:

#!/bin/bash add() { local sum=$(( $1 + $2 )) echo "$sum" } result=$(add 5 3) echo "Sum: $result" # Using return (for exit status only) is_even() { if [ $(( $1 % 2 )) -eq 0 ]; then return 0 # true (success) else return 1 # false (failure) fi } if is_even 4; then echo "4 is even" fi

Exercise 5.4: Factorial Function

Problem: Write a recursive function to calculate factorial.

Hint: Call the function from itself.

Solution:

#!/bin/bash factorial() { if [ $1 -le 1 ]; then echo 1 else local prev=$(factorial $(($1 - 1))) echo $(($1 * prev)) fi } echo "Enter a number:" read n result=$(factorial $n) echo "$n! = $result"

Exercise 5.5: Local vs Global Variables

Problem: Demonstrate local and global variable scope.

Hint: Use local keyword inside functions.

Solution:

#!/bin/bash global_var="I'm global" test_scope() { local local_var="I'm local" global_var="Modified global" echo "Inside function:" echo " global_var = $global_var" echo " local_var = $local_var" } test_scope echo "Outside function:" echo " global_var = $global_var" echo " local_var = $local_var" # Empty

Exercise 5.6: Calculator Using Functions

Problem: Create a calculator with separate functions for each operation.

Hint: Define functions for add, subtract, multiply, divide.

Solution:

#!/bin/bash add() { echo $(($1 + $2)) } subtract() { echo $(($1 - $2)) } multiply() { echo $(($1 * $2)) } divide() { if [ $2 -eq 0 ]; then echo "Error: Division by zero" return 1 fi echo "scale=2; $1 / $2" | bc } # Main menu while true; do echo "1) Add" echo "2) Subtract" echo "3) Multiply" echo "4) Divide" echo "5) Exit" read -p "Choose: " choice if [ $choice -eq 5 ]; then break fi read -p "Enter first number: " a read -p "Enter second number: " b case $choice in 1) result=$(add $a $b) ;; 2) result=$(subtract $a $b) ;; 3) result=$(multiply $a $b) ;; 4) result=$(divide $a $b) ;; *) echo "Invalid choice"; continue ;; esac echo "Result: $result" echo done

Exercise 5.7: Validation Function

Problem: Create a function that validates if a string is a valid email.

Hint: Use regex pattern matching.

Solution:

#!/bin/bash validate_email() { local email="$1" # Basic email regex if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then return 0 # Valid else return 1 # Invalid fi } echo "Enter email address:" read email if validate_email "$email"; then echo "✓ Valid email address" else echo "✗ Invalid email address" fi

Exercise 5.8: Array Processing Function

Problem: Create a function that finds the maximum value in an array.

Hint: Pass array as argument, use loop to find max.

Solution:

#!/bin/bash find_max() { local max=$1 shift for num in "$@"; do if [ $num -gt $max ]; then max=$num fi done echo $max } # Test array numbers=(23 45 12 67 34 89 5) echo "Array: ${numbers[@]}" max=$(find_max "${numbers[@]}") echo "Maximum value: $max"

Exercise 5.9: Function Library

Problem: Create a library of math functions in a separate file and source it.

Hint: Create a file with functions and use source or . to include.

Solution:

# mathlib.sh - Library file #!/bin/bash square() { echo $(($1 * $1)) } cube() { echo $(($1 * $1 * $1)) } is_prime() { if [ $1 -lt 2 ]; then return 1 fi for ((i=2; i*i<=$1; i++)); do if [ $(($1 % i)) -eq 0 ]; then return 1 fi done return 0 } # Main script #!/bin/bash source ./mathlib.sh # or . ./mathlib.sh echo "Enter a number:" read num echo "Square: $(square $num)" echo "Cube: $(cube $num)" if is_prime $num; then echo "$num is prime" else echo "$num is not prime" fi

6. File Operations

Exercise 6.1: File Existence Check

Problem: Check if a file exists and is readable.

Hint: Use -e and -r test operators.

Solution:

#!/bin/bash echo "Enter filename:" read filename if [ -e "$filename" ]; then echo "✓ File exists" if [ -r "$filename" ]; then echo "✓ File is readable" else echo "✗ File is not readable" fi if [ -w "$filename" ]; then echo "✓ File is writable" else echo "✗ File is not writable" fi if [ -x "$filename" ]; then echo "✓ File is executable" else echo "✗ File is not executable" fi else echo "✗ File does not exist" fi

Exercise 6.2: File Backup

Problem: Create a backup of a file with timestamp.

Hint: Use cp and date command for timestamp.

Solution:

#!/bin/bash echo "Enter filename to backup:" read filename if [ ! -f "$filename" ]; then echo "File not found!" exit 1 fi timestamp=$(date +%Y%m%d_%H%M%S) backup="${filename}_${timestamp}.bak" cp "$filename" "$backup" echo "Backup created: $backup" # List backups echo -e "\nExisting backups:" ls -l "${filename}_"*.bak 2>/dev/null || echo "No backups found"

Exercise 6.3: File Information

Problem: Display detailed information about a file.

Hint: Use stat command or parse ls -l.

Solution:

#!/bin/bash echo "Enter filename:" read filename if [ ! -e "$filename" ]; then echo "File not found!" exit 1 fi echo "=== File Information ===" echo "Filename: $filename" echo "Full path: $(realpath "$filename")" echo "Size: $(stat -c%s "$filename" 2>/dev/null || stat -f%z "$filename") bytes" echo "Permissions: $(stat -c%A "$filename" 2>/dev/null || stat -f%Sp "$filename")" echo "Owner: $(stat -c%U "$filename" 2>/dev/null || stat -f%Su "$filename")" echo "Group: $(stat -c%G "$filename" 2>/dev/null || stat -f%Sg "$filename")" echo "Last modified: $(stat -c%y "$filename" 2>/dev/null || stat -f%Sm "$filename")"

Exercise 6.4: Line Count

Problem: Count the number of lines, words, and characters in a file.

Hint: Use wc command.

Solution:

#!/bin/bash echo "Enter filename:" read filename if [ ! -f "$filename" ]; then echo "File not found!" exit 1 fi lines=$(wc -l < "$filename") words=$(wc -w < "$filename") chars=$(wc -m < "$filename") echo "=== File Statistics ===" echo "Lines: $lines" echo "Words: $words" echo "Characters: $chars" # Display file preview echo -e "\n=== File Preview (first 5 lines) ===" head -5 "$filename"

Exercise 6.5: Search in Files

Problem: Search for a pattern in all .txt files in current directory.

Hint: Use grep with loop.

Solution:

#!/bin/bash echo "Enter search pattern:" read pattern found=0 for file in *.txt; do if [ -f "$file" ]; then if grep -q "$pattern" "$file"; then echo "✓ Pattern found in: $file" grep --color=auto -n "$pattern" "$file" | head -3 echo "---" ((found++)) fi fi done if [ $found -eq 0 ]; then echo "Pattern not found in any .txt files" else echo "Found in $found file(s)" fi

Exercise 6.6: File Organizer

Problem: Organize files by extension into subdirectories.

Hint: Create directories based on file extensions and move files.

Solution:

#!/bin/bash echo "Organizing files by extension..." for file in *; do if [ -f "$file" ]; then # Get extension ext="${file##*.}" # Skip if no extension if [ "$ext" = "$file" ]; then ext="no_extension" fi # Create directory if it doesn't exist mkdir -p "$ext" # Move file mv -v "$file" "$ext/" fi done echo "Organization complete!" echo "Directories created:" ls -ld */ 2>/dev/null

Exercise 6.7: Batch Rename Files

Problem: Rename all .txt files to add a prefix "backup_".

Hint: Loop through files and use mv.

Solution:

#!/bin/bash prefix="backup_" echo "Renaming .txt files with prefix '$prefix'..." count=0 for file in *.txt; do if [ -f "$file" ]; then newname="${prefix}${file}" mv -v "$file" "$newname" ((count++)) fi done echo "Renamed $count file(s)" # Undo function undo_rename() { for file in ${prefix}*.txt; do if [ -f "$file" ]; then original="${file#$prefix}" mv -v "$file" "$original" fi done }

Exercise 6.8: Directory Tree

Problem: Create a directory tree structure.

Hint: Use mkdir -p for nested directories.

Solution:

#!/bin/bash echo "Creating project structure..." mkdir -p project/{src,bin,docs,tests,config} mkdir -p project/src/{main,utils,models} mkdir -p project/tests/{unit,integration} # Create some placeholder files touch project/src/main/app.py touch project/src/utils/helpers.py touch project/README.md touch project/.gitignore touch project/config/{dev,prod}.conf echo "Project structure created:" tree project 2>/dev/null || find project -type d | sort

Exercise 6.9: File Monitor

Problem: Monitor a file for changes and display new lines.

Hint: Use tail -f or loop with diff.

Solution:

#!/bin/bash echo "Enter filename to monitor:" read filename if [ ! -f "$filename" ]; then echo "File not found!" exit 1 fi echo "Monitoring $filename for changes (Ctrl+C to stop)" echo "==================================================" # Method 1: Using tail -f tail -f "$filename" # Method 2: Manual monitoring with diff # last_size=$(stat -c%s "$filename") # while true; do # current_size=$(stat -c%s "$filename") # if [ $current_size -gt $last_size ]; then # echo "File changed!" # tail -c +$((last_size + 1)) "$filename" # last_size=$current_size # fi # sleep 1 # done

Exercise 6.10: CSV File Parser

Problem: Parse a CSV file and display specific columns.

Hint: Use IFS=',' read for parsing.

Solution:

#!/bin/bash echo "Enter CSV filename:" read filename if [ ! -f "$filename" ]; then echo "File not found!" exit 1 fi # Read and display header read header < "$filename" echo "Columns:" IFS=',' read -ra columns <<< "$header" for i in "${!columns[@]}"; do echo "$i: ${columns[$i]}" done echo -e "\nEnter column numbers to display (space-separated):" read -a cols_to_show echo -e "\nSelected data:" line_num=1 while IFS=',' read -ra values; do echo -n "Record $line_num: " for col in "${cols_to_show[@]}"; do echo -n "${values[$col]} " done echo ((line_num++)) done < "$filename"

7. Text Processing

Exercise 7.1: Word Count

Problem: Count occurrences of each word in a file.

Hint: Use tr to split words, then sort and uniq -c.

Solution:

#!/bin/bash echo "Enter filename:" read filename if [ ! -f "$filename" ]; then echo "File not found!" exit 1 fi echo "Word frequency:" echo "================" tr '[:space:]' '\n' < "$filename" | \ tr -d '[:punct:]' | \ tr '[:upper:]' '[:lower:]' | \ grep -v '^$' | \ sort | \ uniq -c | \ sort -rn | \ head -20

Exercise 7.2: Extract Email Addresses

Problem: Extract all email addresses from a file.

Hint: Use grep -o with email pattern.

Solution:

#!/bin/bash echo "Enter filename:" read filename if [ ! -f "$filename" ]; then echo "File not found!" exit 1 fi echo "Email addresses found:" echo "=======================" grep -oE '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' "$filename" | sort -u count=$(grep -cE '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' "$filename") echo -e "\nTotal unique emails: $count"

Exercise 7.3: Log File Analyzer

Problem: Analyze a log file for error patterns.

Hint: Use grep to find errors and count occurrences.

Solution:

#!/bin/bash echo "Enter log filename:" read filename if [ ! -f "$filename" ]; then echo "File not found!" exit 1 fi echo "=== Log Analysis ===" echo "Total lines: $(wc -l < "$filename")" echo # Error levels echo "Error levels:" for level in ERROR WARN INFO DEBUG; do count=$(grep -c "$level" "$filename") echo " $level: $count" done echo # Top error messages echo "Top error messages:" grep -i error "$filename" | \ sed 's/.*ERROR: //' | \ cut -d' ' -f1-5 | \ sort | \ uniq -c | \ sort -rn | \ head -10 # Errors by hour echo -e "\nErrors by hour:" grep -i error "$filename" | \ grep -o ' [0-2][0-9]:[0-5][0-9]:[0-5][0-9]' | \ cut -d: -f1 | \ sort | \ uniq -c

Exercise 7.4: Find and Replace

Problem: Replace all occurrences of a string in a file.

Hint: Use sed for substitution.

Solution:

#!/bin/bash echo "Enter filename:" read filename echo "Enter search string:" read search echo "Enter replacement string:" read replace if [ ! -f "$filename" ]; then echo "File not found!" exit 1 fi echo "Preview of changes (first 5 lines):" sed "s/$search/$replace/g" "$filename" | head -5 echo -e "\nMake changes? (y/n)" read answer if [ "$answer" = "y" ]; then # Create backup cp "$filename" "$filename.bak" echo "Backup created: $filename.bak" # Perform replacement sed -i "s/$search/$replace/g" "$filename" echo "Replacement complete!" # Show changes echo -e "\nChanged lines:" diff "$filename.bak" "$filename" | grep '^>' else echo "Operation cancelled" fi

Exercise 7.5: CSV to JSON Converter

Problem: Convert a CSV file to JSON format.

Hint: Parse CSV and build JSON structure.

Solution:

#!/bin/bash echo "Enter CSV filename:" read filename if [ ! -f "$filename" ]; then echo "File not found!" exit 1 fi output="${filename%.csv}.json" # Read header read header < "$filename" IFS=',' read -ra columns <<< "$header" echo "Converting $filename to JSON..." # Start JSON array echo "[" > "$output" line_num=1 while IFS=',' read -ra values; do # Skip header if [ $line_num -eq 1 ]; then ((line_num++)) continue fi # Start object echo " {" >> "$output" # Add fields for i in "${!columns[@]}"; do comma="," if [ $i -eq $((${#columns[@]}-1)) ]; then comma="" fi echo " \"${columns[$i]}\": \"${values[$i]}\"$comma" >> "$output" done # End object comma="," if [ $line_num -eq $(wc -l < "$filename") ]; then comma="" fi echo " }$comma" >> "$output" ((line_num++)) done < "$filename" # End JSON array echo "]" >> "$output" echo "Conversion complete! Output saved to $output"

Exercise 7.6: Text Wrapper

Problem: Wrap text at a specified column width.

Hint: Use fold command or implement custom wrapping.

Solution:

#!/bin/bash echo "Enter filename:" read filename echo "Enter maximum line width (default 80):" read width width=${width:-80} if [ ! -f "$filename" ]; then echo "File not found!" exit 1 fi echo "Wrapped text (width $width):" echo "==============================" while IFS= read -r line; do while [ ${#line} -gt $width ]; do # Find last space before width pos=$width while [ $pos -gt 0 ] && [ "${line:$pos:1}" != " " ]; do ((pos--)) done if [ $pos -eq 0 ]; then # No space found, force break at width pos=$width fi echo "${line:0:$pos}" line="${line:$pos}" # Remove leading space line="${line# }" done echo "$line" done < "$filename"

Exercise 7.7: Remove Duplicate Lines

Problem: Remove duplicate lines from a file while preserving order.

Hint: Use awk or associative array.

Solution:

#!/bin/bash echo "Enter filename:" read filename if [ ! -f "$filename" ]; then echo "File not found!" exit 1 fi output="${filename%.txt}_unique.txt" echo "Removing duplicates (first occurrence preserved)..." # Using awk awk '!seen[$0]++' "$filename" > "$output" original=$(wc -l < "$filename") unique=$(wc -l < "$output") removed=$((original - unique)) echo "Original lines: $original" echo "Unique lines: $unique" echo "Duplicates removed: $removed" echo "Output saved to $output" # Show preview echo -e "\nPreview of cleaned file (first 10 lines):" head -10 "$output"

8. Arrays and Data Structures

Exercise 8.1: Array Basics

Problem: Create and manipulate a basic indexed array.

Hint: Declare array with () and access with ${array[index]}.

Solution:

#!/bin/bash # Create array fruits=("apple" "banana" "orange" "grape" "mango") # Display all elements echo "All fruits: ${fruits[@]}" # Display individual elements echo "First fruit: ${fruits[0]}" echo "Third fruit: ${fruits[2]}" # Array length echo "Number of fruits: ${#fruits[@]}" # Array indices echo "Indices: ${!fruits[@]}" # Add element fruits+=("pineapple") echo "After adding: ${fruits[@]}" # Remove element unset fruits[1] echo "After removing index 1: ${fruits[@]}"

Exercise 8.2: Array Operations

Problem: Perform common array operations: sum, average, min, max.

Hint: Loop through array elements.

Solution:

#!/bin/bash numbers=(23 45 12 67 34 89 5 18 42) echo "Array: ${numbers[@]}" # Sum sum=0 for num in "${numbers[@]}"; do sum=$((sum + num)) done echo "Sum: $sum" # Average avg=$((sum / ${#numbers[@]})) echo "Average: $avg" # Min/Max min=${numbers[0]} max=${numbers[0]} for num in "${numbers[@]}"; do if [ $num -lt $min ]; then min=$num fi if [ $num -gt $max ]; then max=$num fi done echo "Minimum: $min" echo "Maximum: $max"

Exercise 8.3: Array Sorting

Problem: Sort an array of numbers.

Hint: Use bubble sort or pipe to sort.

Solution:

#!/bin/bash numbers=(23 45 12 67 34 89 5 18 42) echo "Original array: ${numbers[@]}" # Method 1: Using sort command (works for simple cases) sorted=($(printf '%s\n' "${numbers[@]}" | sort -n)) echo "Sorted (sort command): ${sorted[@]}" # Method 2: Bubble sort bubble_sort() { local arr=("$@") local n=${#arr[@]} for ((i=0; i<n-1; i++)); do for ((j=0; j<n-i-1; j++)); do if [ ${arr[$j]} -gt ${arr[$((j+1))]} ]; then # Swap temp=${arr[$j]} arr[$j]=${arr[$((j+1))]} arr[$((j+1))]=$temp fi done done echo "${arr[@]}" } echo "Sorted (bubble sort): $(bubble_sort "${numbers[@]}")"

Exercise 8.4: Associative Arrays

Problem: Use associative arrays to store key-value pairs.

Hint: Declare with declare -A (Bash 4+).

Solution:

#!/bin/bash # Requires Bash 4+ declare -A capitals capitals["USA"]="Washington" capitals["France"]="Paris" capitals["Japan"]="Tokyo" capitals["UK"]="London" capitals["Germany"]="Berlin" # List all keys echo "Countries: ${!capitals[@]}" # List all values echo "Capitals: ${capitals[@]}" # Iterate over key-value pairs for country in "${!capitals[@]}"; do echo "$country: ${capitals[$country]}" done # Get specific value echo "Capital of France: ${capitals[France]}" # Check if key exists if [ -n "${capitals[Italy]+_}" ]; then echo "Italy is in the list" else echo "Italy is not in the list" fi

Exercise 8.5: Stack Implementation

Problem: Implement a stack data structure using arrays.

Hint: Use array with push/pop operations.

Solution:

#!/bin/bash declare -a stack push() { stack+=("$1") echo "Pushed: $1" } pop() { if [ ${#stack[@]} -eq 0 ]; then echo "Stack is empty" return 1 fi top_index=$((${#stack[@]} - 1)) popped="${stack[$top_index]}" unset stack[$top_index] # Reindex array stack=("${stack[@]}") echo "Popped: $popped" } peek() { if [ ${#stack[@]} -eq 0 ]; then echo "Stack is empty" else echo "Top element: ${stack[-1]}" fi } # Test the stack push 10 push 20 push 30 peek echo "Stack size: ${#stack[@]}" pop peek echo "Stack: ${stack[@]}"

Exercise 8.6: Queue Implementation

Problem: Implement a queue data structure.

Hint: Use array with enqueue/dequeue operations.

Solution:

#!/bin/bash declare -a queue enqueue() { queue+=("$1") echo "Enqueued: $1" } dequeue() { if [ ${#queue[@]} -eq 0 ]; then echo "Queue is empty" return 1 fi dequeued="${queue[0]}" # Remove first element queue=("${queue[@]:1}") echo "Dequeued: $dequeued" } front() { if [ ${#queue[@]} -eq 0 ]; then echo "Queue is empty" else echo "Front element: ${queue[0]}" fi } # Test the queue enqueue "first" enqueue "second" enqueue "third" front echo "Queue size: ${#queue[@]}" dequeue front echo "Queue: ${queue[@]}"

Exercise 8.7: Matrix Operations

Problem: Create and manipulate a 2D matrix.

Hint: Use arrays of arrays or indexed arithmetic.

Solution:

#!/bin/bash # Create a 3x3 matrix declare -a matrix fill_matrix() { local val=1 for ((i=0; i<3; i++)); do for ((j=0; j<3; j++)); do matrix[$((i*3 + j))]=$val ((val++)) done done } print_matrix() { for ((i=0; i<3; i++)); do for ((j=0; j<3; j++)); do echo -n "${matrix[$((i*3 + j))]} " done echo done } get_element() { local row=$1 local col=$2 echo "${matrix[$((row*3 + col))]}" } set_element() { local row=$1 local col=$2 local val=$3 matrix[$((row*3 + col))]=$val } fill_matrix echo "Original matrix:" print_matrix set_element 1 1 99 echo -e "\nAfter setting element [1,1] to 99:" print_matrix echo -e "\nElement at [0,2]: $(get_element 0 2)"

9. Advanced Scripting

Exercise 9.1: Command Line Options Parser

Problem: Parse command line options with getopts.

Hint: Use getopts in a while loop.

Solution:

#!/bin/bash usage() { echo "Usage: $0 [-h] [-v] [-f filename] [-n count]" echo " -h Show help" echo " -v Verbose mode" echo " -f Specify filename" echo " -n Number of iterations" } verbose=0 filename="" count=1 while getopts "hvf:n:" opt; do case $opt in h) usage exit 0 ;; v) verbose=1 ;; f) filename="$OPTARG" ;; n) count="$OPTARG" ;; \?) echo "Invalid option: -$OPTARG" >&2 usage exit 1 ;; :) echo "Option -$OPTARG requires an argument" >&2 exit 1 ;; esac done echo "Verbose mode: $verbose" echo "Filename: $filename" echo "Count: $count"

Exercise 9.2: Signal Handling

Problem: Handle signals like SIGINT (Ctrl+C) gracefully.

Hint: Use trap to catch signals.

Solution:

#!/bin/bash cleanup() { echo -e "\nCleaning up temporary files..." rm -f /tmp/temp_$$.* echo "Done. Exiting." exit 0 } # Set trap for multiple signals trap cleanup SIGINT SIGTERM EXIT # Create temporary files echo "Creating temporary files..." touch /tmp/temp_$$.log touch /tmp/temp_$$.tmp echo "Files created. Press Ctrl+C to interrupt." # Simulate work count=0 while true; do echo "Working... ($count)" sleep 1 ((count++)) done

Exercise 9.3: Multi-threading with Background Jobs

Problem: Run multiple tasks in parallel using background jobs.

Hint: Use & to run jobs in background and wait to synchronize.

Solution:

#!/bin/bash task() { local id=$1 local duration=$((RANDOM % 5 + 1)) echo "Task $id starting (will run for $duration seconds)" sleep $duration echo "Task $id completed" return $((id * 10)) } echo "Starting parallel tasks..." # Start tasks in background task 1 & pid1=$! task 2 & pid2=$! task 3 & pid3=$! echo "Waiting for tasks to complete..." # Wait for specific task wait $pid1 echo "Task 1 finished with status $?" # Wait for all remaining tasks wait echo "All tasks completed!" # Job control jobs

Exercise 9.4: Progress Bar

Problem: Create a progress bar for long-running operations.

Hint: Use printf with carriage return \r.

Solution:

#!/bin/bash progress_bar() { local current=$1 local total=$2 local width=50 percent=$((current * 100 / total)) completed=$((current * width / total)) remaining=$((width - completed)) printf "\rProgress: [" printf "%${completed}s" | tr ' ' '#' printf "%${remaining}s" | tr ' ' '-' printf "] %d%%" $percent } # Simulate work total=100 for ((i=1; i<=total; i++)); do # Some work here sleep 0.1 progress_bar $i $total done echo -e "\nDone!"

Exercise 9.5: Config File Parser

Problem: Parse a configuration file with key=value pairs.

Hint: Read file line by line and parse with IFS.

Solution:

#!/bin/bash CONFIG_FILE="app.conf" # Create sample config if not exists if [ ! -f "$CONFIG_FILE" ]; then cat > "$CONFIG_FILE" << 'EOF' # Application configuration DB_HOST=localhost DB_PORT=5432 DB_NAME=myapp MAX_CONNECTIONS=100 DEBUG=true LOG_FILE=/var/log/app.log EOF echo "Created sample config file: $CONFIG_FILE" fi # Parse config file declare -A config while IFS='=' read -r key value; do # Skip comments and empty lines [[ "$key" =~ ^#.*$ || -z "$key" ]] && continue # Trim whitespace key=$(echo "$key" | xargs) value=$(echo "$value" | xargs) config["$key"]="$value" done < "$CONFIG_FILE" # Display configuration echo "=== Configuration ===" for key in "${!config[@]}"; do echo "$key = ${config[$key]}" done # Use configuration echo -e "\n=== Using Configuration ===" echo "Connecting to ${config[DB_HOST]}:${config[DB_PORT]}" echo "Database: ${config[DB_NAME]}" echo "Debug mode: ${config[DEBUG]}"

Exercise 9.6: Interactive Menu System

Problem: Create a multi-level interactive menu system.

Hint: Use functions for each menu level.

Solution:

#!/bin/bash main_menu() { while true; do clear echo "=== Main Menu ===" echo "1) System Information" echo "2) File Operations" echo "3) Network Tools" echo "4) Exit" read -p "Choice: " choice case $choice in 1) system_menu ;; 2) file_menu ;; 3) network_menu ;; 4) echo "Goodbye!"; exit 0 ;; *) echo "Invalid choice"; sleep 1 ;; esac done } system_menu() { while true; do clear echo "=== System Information ===" echo "1) Disk Usage" echo "2) Memory Usage" echo "3) CPU Info" echo "4) Back to Main" read -p "Choice: " choice case $choice in 1) df -h; read -p "Press Enter to continue..." ;; 2) free -h; read -p "Press Enter to continue..." ;; 3) lscpu | head -10; read -p "Press Enter to continue..." ;; 4) break ;; *) echo "Invalid choice"; sleep 1 ;; esac done } file_menu() { while true; do clear echo "=== File Operations ===" echo "1) List Files" echo "2) Show Current Directory" echo "3) Create Directory" echo "4) Back to Main" read -p "Choice: " choice case $choice in 1) ls -la; read -p "Press Enter to continue..." ;; 2) pwd; read -p "Press Enter to continue..." ;; 3) read -p "Enter directory name: " dirname mkdir -p "$dirname" echo "Directory created" sleep 1 ;; 4) break ;; *) echo "Invalid choice"; sleep 1 ;; esac done } network_menu() { while true; do clear echo "=== Network Tools ===" echo "1) Ping Host" echo "2) Show Network Interfaces" echo "3) DNS Lookup" echo "4) Back to Main" read -p "Choice: " choice case $choice in 1) read -p "Enter host: " host ping -c 4 "$host" read -p "Press Enter to continue..." ;; 2) ip addr show; read -p "Press Enter to continue..." ;; 3) read -p "Enter domain: " domain nslookup "$domain" read -p "Press Enter to continue..." ;; 4) break ;; *) echo "Invalid choice"; sleep 1 ;; esac done } # Start the application main_menu

10. System Administration

Exercise 10.1: Disk Usage Monitor

Problem: Monitor disk usage and alert if threshold exceeded.

Hint: Use df and parse percentage.

Solution:

#!/bin/bash THRESHOLD=80 EMAIL="admin@localhost" check_disk_usage() { df -h | grep -vE '^Filesystem|tmpfs|cdrom' | while read line; do usage=$(echo "$line" | awk '{print $5}' | sed 's/%//') mount=$(echo "$line" | awk '{print $6}') fs=$(echo "$line" | awk '{print $1}') if [ $usage -ge $THRESHOLD ]; then echo "WARNING: Filesystem $fs mounted on $mount is at ${usage}% capacity" # Send alert (requires mail setup) # echo "Filesystem $fs on $mount is at ${usage}% capacity" | mail -s "Disk Space Alert" "$EMAIL" fi done } check_disk_usage

Exercise 10.2: Process Manager

Problem: List and manage processes by name.

Hint: Use pgrep and pkill or ps with grep.

Solution:

#!/bin/bash list_processes() { echo "=== Process List ===" ps aux | head -1 ps aux | grep -E "$1" | grep -v grep } kill_processes() { echo "Killing processes matching: $1" pkill -f "$1" && echo "Done" || echo "No processes found" } while true; do clear echo "=== Process Manager ===" echo "1) List all processes" echo "2) Search processes by name" echo "3) Kill processes by name" echo "4) Show process tree" echo "5) Exit" read -p "Choice: " choice case $choice in 1) ps aux | head -20 read -p "Press Enter to continue..." ;; 2) read -p "Enter process name: " name list_processes "$name" read -p "Press Enter to continue..." ;; 3) read -p "Enter process name to kill: " name kill_processes "$name" sleep 2 ;; 4) pstree | head -20 read -p "Press Enter to continue..." ;; 5) exit 0 ;; esac done

Exercise 10.3: User Management Script

Problem: Create a script to add/remove/list users.

Hint: Use useradd, userdel, and parse /etc/passwd.

Solution:

#!/bin/bash # Must be run as root if [ $EUID -ne 0 ]; then echo "This script must be run as root" exit 1 fi list_users() { echo "=== System Users ===" awk -F: '{print $1 " (UID: " $3 ")"}' /etc/passwd | sort } add_user() { read -p "Enter username: " username read -s -p "Enter password: " password echo useradd -m -s /bin/bash "$username" echo "$username:$password" | chpasswd echo "User $username created successfully" } delete_user() { read -p "Enter username to delete: " username read -p "Remove home directory? (y/n): " remove_home if [ "$remove_home" = "y" ]; then userdel -r "$username" else userdel "$username" fi echo "User $username deleted" } while true; do clear echo "=== User Management ===" echo "1) List users" echo "2) Add user" echo "3) Delete user" echo "4) Exit" read -p "Choice: " choice case $choice in 1) list_users; read -p "Press Enter..." ;; 2) add_user; read -p "Press Enter..." ;; 3) delete_user; read -p "Press Enter..." ;; 4) exit 0 ;; esac done

Exercise 10.4: Service Monitor

Problem: Monitor system services and restart if down.

Hint: Use systemctl to check service status.

Solution:

#!/bin/bash SERVICES=("nginx" "postgresql" "redis") LOG_FILE="/var/log/service_monitor.log" check_service() { local service=$1 if systemctl is-active --quiet "$service"; then echo "$(date): $service is running" >> "$LOG_FILE" return 0 else echo "$(date): $service is DOWN!" >> "$LOG_FILE" return 1 fi } restart_service() { local service=$1 echo "$(date): Attempting to restart $service" >> "$LOG_FILE" systemctl restart "$service" sleep 5 if systemctl is-active --quiet "$service"; then echo "$(date): $service successfully restarted" >> "$LOG_FILE" return 0 else echo "$(date): Failed to restart $service" >> "$LOG_FILE" return 1 fi } while true; do for service in "${SERVICES[@]}"; do if ! check_service "$service"; then restart_service "$service" fi done sleep 60 done

Exercise 10.5: Log Rotator

Problem: Rotate log files when they exceed a size limit.

Hint: Check file size and move with timestamp.

Solution:

#!/bin/bash LOG_DIR="/var/log/myapp" MAX_SIZE=$((10 * 1024 * 1024)) # 10MB RETENTION_DAYS=7 rotate_log() { local logfile=$1 if [ ! -f "$logfile" ]; then return fi size=$(stat -c%s "$logfile" 2>/dev/null || stat -f%z "$logfile" 2>/dev/null) if [ $size -gt $MAX_SIZE ]; then timestamp=$(date +%Y%m%d_%H%M%S) rotated="${logfile}.$timestamp" echo "Rotating $logfile (size: $size bytes)" mv "$logfile" "$rotated" gzip "$rotated" touch "$logfile" # Clean old logs find "$LOG_DIR" -name "*.gz" -mtime +$RETENTION_DAYS -delete fi } # Rotate all .log files in directory for logfile in "$LOG_DIR"/*.log; do rotate_log "$logfile" done

11. Real-World Projects

Project 11.1: System Information Dashboard

Problem: Create a comprehensive system information dashboard.

Solution:

#!/bin/bash show_header() { clear echo "══════════════════════════════════════════════════" echo " SYSTEM INFORMATION DASHBOARD " echo "══════════════════════════════════════════════════" echo "Hostname: $(hostname)" echo "User: $(whoami)" echo "Date: $(date '+%Y-%m-%d %H:%M:%S')" echo "Uptime: $(uptime | awk '{print $3,$4}' | sed 's/,//')" echo "══════════════════════════════════════════════════" } show_cpu() { echo echo "─── CPU Information ──────────────────────────────" echo "Model: $(lscpu | grep "Model name" | cut -d: -f2 | xargs)" echo "Cores: $(nproc)" echo "Load Average: $(uptime | awk -F'load average:' '{print $2}')" echo "Current Usage: $(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)%" } show_memory() { echo echo "─── Memory Information ───────────────────────────" free -h | awk ' NR==1 {printf "%-10s %10s %10s %10s %10s\n", $1, $2, $3, $4, $6} NR==2 {printf "%-10s %10s %10s %10s %10s\n", $1, $2, $3, $4, $6} NR==3 {printf "%-10s %10s %10s %10s %10s\n", $1, $2, $3, $4, $5} ' } show_disk() { echo echo "─── Disk Usage ───────────────────────────────────" df -h | grep -vE '^Filesystem|tmpfs|cdrom' | awk '{printf "%-20s %10s %10s %10s %s\n", $1, $2, $3, $4, $6}' } show_network() { echo echo "─── Network Information ──────────────────────────" ip -4 addr show | grep inet | awk '{print "IP: " $2}' | sed 's/\/.*//' echo "Default Gateway: $(ip route | grep default | awk '{print $3}')" echo "DNS Servers: $(grep nameserver /etc/resolv.conf | awk '{print $2}' | tr '\n' ' ')" } show_processes() { echo echo "─── Top Processes by CPU ─────────────────────────" ps aux --sort=-%cpu | head -6 | awk '{printf "%-10s %-10s %5s%% %5s%% %s\n", $1, $2, $3, $4, $11}' } show_services() { echo echo "─── Critical Services ────────────────────────────" for service in sshd cron rsyslog; do if systemctl is-active --quiet "$service"; then echo "✓ $service: running" else echo "✗ $service: stopped" fi done } # Main loop while true; do show_header show_cpu show_memory show_disk show_network show_processes show_services echo echo "══════════════════════════════════════════════════" echo "Press Ctrl+C to exit. Refreshing in 5 seconds..." sleep 5 done

Project 11.2: Backup System with Rotation

Problem: Create a comprehensive backup system with rotation and logging.

Solution:

#!/bin/bash # backup.sh - Automated backup system # Configuration BACKUP_DIR="/backup" SOURCE_DIRS=("/home" "/etc" "/var/log") EXCLUDE=("*.tmp" "*.cache" ".git") RETENTION_DAYS=7 LOG_FILE="/var/log/backup.log" EMAIL="admin@localhost" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } error() { echo -e "${RED}[ERROR]${NC} $1" log "ERROR: $1" } success() { echo -e "${GREEN}[SUCCESS]${NC} $1" log "SUCCESS: $1" } warn() { echo -e "${YELLOW}[WARNING]${NC} $1" log "WARNING: $1" } check_prerequisites() { log "Checking prerequisites..." # Check if running as root if [ $EUID -ne 0 ]; then error "This script must be run as root" exit 1 fi # Check backup directory if [ ! -d "$BACKUP_DIR" ]; then mkdir -p "$BACKUP_DIR" success "Created backup directory: $BACKUP_DIR" fi # Check available space available=$(df "$BACKUP_DIR" | awk 'NR==2 {print $4}') if [ $available -lt 1048576 ]; then # 1GB in KB warn "Low disk space on backup destination" fi } create_backup() { local timestamp=$(date +%Y%m%d_%H%M%S) local backup_file="$BACKUP_DIR/backup_$timestamp.tar.gz" local file_list="$BACKUP_DIR/filelist_$timestamp.txt" log "Starting backup: $backup_file" # Build exclude options exclude_opts=() for pattern in "${EXCLUDE[@]}"; do exclude_opts+=("--exclude=$pattern") done # Create file list find "${SOURCE_DIRS[@]}" -type f > "$file_list" file_count=$(wc -l < "$file_list") log "Found $file_count files to backup" # Create backup with progress tar -czf "$backup_file" "${exclude_opts[@]}" "${SOURCE_DIRS[@]}" 2>&1 | \ while read line; do echo -n "." done echo if [ $? -eq 0 ]; then size=$(du -h "$backup_file" | cut -f1) success "Backup created: $backup_file ($size)" # Create checksum md5sum "$backup_file" > "${backup_file}.md5" # Log backup info { echo "Backup: $backup_file" echo "Date: $(date)" echo "Size: $size" echo "Files: $file_count" echo "Directories: ${SOURCE_DIRS[*]}" echo "---" } >> "$BACKUP_DIR/backup_history.log" else error "Backup failed" rm -f "$backup_file" return 1 fi } rotate_backups() { log "Rotating old backups..." cd "$BACKUP_DIR" || return # Delete backups older than retention period old_backups=$(find . -name "backup_*.tar.gz" -type f -mtime +$RETENTION_DAYS) if [ -n "$old_backups" ]; then echo "$old_backups" | while read backup; do rm -f "$backup" "${backup}.md5" log "Deleted old backup: $backup" done success "Old backups rotated" else log "No backups to rotate" fi } verify_backups() { log "Verifying recent backups..." # Check last 5 backups for backup in $(ls -t "$BACKUP_DIR"/backup_*.tar.gz 2>/dev/null | head -5); do if [ -f "${backup}.md5" ]; then if md5sum -c "${backup}.md5" >/dev/null 2>&1; then success "Verified: $(basename "$backup")" else error "Checksum failed: $(basename "$backup")" fi fi done } send_report() { local report="/tmp/backup_report_$$.txt" { echo "Backup Report - $(date)" echo "========================" echo echo "Status: $1" echo echo "Last 10 backups:" ls -lh "$BACKUP_DIR"/backup_*.tar.gz 2>/dev/null | head -10 echo echo "Backup directory usage:" df -h "$BACKUP_DIR" echo echo "Log tail:" tail -20 "$LOG_FILE" } > "$report" # mail -s "Backup Report" "$EMAIL" < "$report" cat "$report" rm "$report" } # Main execution main() { log "=== Backup Script Started ===" check_prerequisites if create_backup; then rotate_backups verify_backups send_report "SUCCESS" else send_report "FAILED" exit 1 fi log "=== Backup Script Completed ===" } # Run main function main

Project 11.3: Network Scanner

Problem: Create a network scanner to discover hosts and services.

Solution:

#!/bin/bash # network_scanner.sh - Discover hosts and services on network # Configuration NETWORK="192.168.1" TIMEOUT=1 THREADS=10 OUTPUT_DIR="/tmp/scan_results" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # Common ports to scan declare -A PORTS PORTS=( [22]="SSH" [80]="HTTP" [443]="HTTPS" [21]="FTP" [25]="SMTP" [3306]="MySQL" [5432]="PostgreSQL" [8080]="HTTP-Alt" [8443]="HTTPS-Alt" [3389]="RDP" ) create_output_dir() { mkdir -p "$OUTPUT_DIR" rm -f "$OUTPUT_DIR"/host_* } ping_host() { local ip=$1 if ping -c1 -W$TIMEOUT "$ip" >/dev/null 2>&1; then echo "$ip" >> "$OUTPUT_DIR/live_hosts.txt" echo -e "${GREEN}✓${NC} $ip is alive" fi } scan_ports() { local ip=$1 local host_file="$OUTPUT_DIR/host_$ip.txt" for port in "${!PORTS[@]}"; do ( timeout $TIMEOUT bash -c "echo >/dev/tcp/$ip/$port" 2>/dev/null && \ echo "$port:${PORTS[$port]}" >> "$host_file" ) & done wait if [ -s "$host_file" ]; then echo -e "${BLUE} Services on $ip:${NC}" while IFS=: read port service; do echo " - $service ($port)" done < "$host_file" fi } discover_hosts() { echo -e "${YELLOW}Discovering hosts on $NETWORK.0/24...${NC}" # Ping scan with parallel processing for i in {1..254}; do ping_host "$NETWORK.$i" & # Limit parallel processes if [ $((i % THREADS)) -eq 0 ]; then wait fi done wait echo -e "\n${GREEN}Found $(wc -l < "$OUTPUT_DIR/live_hosts.txt") live hosts${NC}" } scan_services() { echo -e "\n${YELLOW}Scanning for services...${NC}" while read ip; do scan_ports "$ip" & # Limit parallel scans if [ $((++count % THREADS)) -eq 0 ]; then wait fi done < "$OUTPUT_DIR/live_hosts.txt" wait } generate_report() { local report_file="$OUTPUT_DIR/network_report_$(date +%Y%m%d_%H%M%S).txt" { echo "Network Scan Report" echo "===================" echo "Scan Date: $(date)" echo "Network: $NETWORK.0/24" echo echo "Live Hosts:" echo "-----------" while read ip; do echo "$ip" if [ -f "$OUTPUT_DIR/host_$ip.txt" ]; then while IFS=: read port service; do echo " └─ $service (port $port)" done < "$OUTPUT_DIR/host_$ip.txt" fi echo done < "$OUTPUT_DIR/live_hosts.txt" echo "Summary:" echo "--------" echo "Total hosts found: $(wc -l < "$OUTPUT_DIR/live_hosts.txt")" echo "Total services detected: $(cat "$OUTPUT_DIR"/host_* 2>/dev/null | wc -l)" } > "$report_file" echo -e "\n${GREEN}Report saved to: $report_file${NC}" } # Main execution main() { clear echo "══════════════════════════════════════════" echo " NETWORK SCANNER v1.0 " echo "══════════════════════════════════════════" create_output_dir discover_hosts scan_services generate_report echo -e "\n${GREEN}Scan complete!${NC}" } # Run main function main

Project 11.4: Log Analyzer with Statistics

Problem: Create a comprehensive log analyzer with statistical reports.

Solution:

#!/bin/bash # log_analyzer.sh - Comprehensive log file analyzer # Configuration LOG_FILE="" REPORT_DIR="./reports" VERBOSE=0 TOP_N=10 # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' usage() { echo "Usage: $0 [-f logfile] [-o output_dir] [-n top_n] [-v]" echo " -f logfile Log file to analyze" echo " -o output_dir Output directory for reports (default: ./reports)" echo " -n top_n Number of top items to show (default: 10)" echo " -v Verbose mode" exit 1 } while getopts "f:o:n:vh" opt; do case $opt in f) LOG_FILE="$OPTARG" ;; o) REPORT_DIR="$OPTARG" ;; n) TOP_N="$OPTARG" ;; v) VERBOSE=1 ;; h) usage ;; *) usage ;; esac done if [ -z "$LOG_FILE" ] || [ ! -f "$LOG_FILE" ]; then echo -e "${RED}Error: Log file not specified or not found${NC}" usage fi create_report_dir() { mkdir -p "$REPORT_DIR" echo -e "${GREEN}Report directory created: $REPORT_DIR${NC}" } analyze_basic_stats() { local report="$REPORT_DIR/basic_stats.txt" echo "Basic Statistics" > "$report" echo "================" >> "$report" echo "Log file: $LOG_FILE" >> "$report" echo "Analysis date: $(date)" >> "$report" echo >> "$report" # Total lines total_lines=$(wc -l < "$LOG_FILE") echo "Total lines: $total_lines" >> "$report" # Non-empty lines non_empty=$(grep -c '.' "$LOG_FILE") echo "Non-empty lines: $non_empty" >> "$report" # Empty lines empty=$((total_lines - non_empty)) echo "Empty lines: $empty" >> "$report" # File size size=$(du -h "$LOG_FILE" | cut -f1) echo "File size: $size" >> "$report" # Average line length avg_length=$(awk '{sum+=length} END {print int(sum/NR)}' "$LOG_FILE") echo "Average line length: $avg_length characters" >> "$report" echo -e "${GREEN}Basic statistics saved to $report${NC}" } analyze_ip_addresses() { local report="$REPORT_DIR/ip_addresses.txt" echo "IP Address Analysis" > "$report" echo "===================" >> "$report" echo >> "$report" # Extract and count IPs grep -oE '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' "$LOG_FILE" | \ sort | uniq -c | sort -rn > "$report.tmp" echo "Top $TOP_N IP addresses:" >> "$report" head -$TOP_N "$report.tmp" >> "$report" total_ips=$(wc -l < "$report.tmp") echo >> "$report" echo "Total unique IPs: $total_ips" >> "$report" rm "$report.tmp" echo -e "${GREEN}IP analysis saved to $report${NC}" } analyze_error_levels() { local report="$REPORT_DIR/error_levels.txt" echo "Error Level Analysis" > "$report" echo "====================" >> "$report" echo >> "$report" # Common log levels for level in ERROR WARN INFO DEBUG FATAL; do count=$(grep -i "$level" "$LOG_FILE" | wc -l) echo "$level: $count" >> "$report" done echo -e "${GREEN}Error level analysis saved to $report${NC}" } analyze_time_patterns() { local report="$REPORT_DIR/time_patterns.txt" echo "Time Pattern Analysis" > "$report" echo "=====================" >> "$report" echo >> "$report" # Hourly distribution (assuming timestamp format HH:MM:SS) grep -o ' [0-2][0-9]:[0-5][0-9]:[0-5][0-9]' "$LOG_FILE" | \ cut -d: -f1 | sort | uniq -c | sort -rn > "$report.tmp" echo "Hourly distribution (top $TOP_N):" >> "$report" head -$TOP_N "$report.tmp" | while read count hour; do echo " Hour $hour: $count entries" >> "$report" done rm "$report.tmp" # Daily distribution if available grep -o '[0-9]{4}-[0-9]{2}-[0-9]{2}' "$LOG_FILE" | \ sort | uniq -c | sort -rn | head -$TOP_N > "$report.daily" if [ -s "$report.daily" ]; then echo >> "$report" echo "Daily distribution:" >> "$report" cat "$report.daily" | while read count date; do echo " $date: $count entries" >> "$report" done fi rm -f "$report.daily" "$report.tmp" echo -e "${GREEN}Time pattern analysis saved to $report${NC}" } analyze_frequent_words() { local report="$REPORT_DIR/frequent_words.txt" echo "Frequent Word Analysis" > "$report" echo "======================" >> "$report" echo >> "$report" # Extract common words (excluding common stop words) tr '[:space:]' '\n' < "$LOG_FILE" | \ tr -d '[:punct:]' | \ tr '[:upper:]' '[:lower:]' | \ grep -E '^[a-z]{4,}$' | \ sort | uniq -c | sort -rn > "$report.tmp" echo "Top $TOP_N most frequent words:" >> "$report" head -$TOP_N "$report.tmp" | while read count word; do echo " $word: $count occurrences" >> "$report" done rm "$report.tmp" echo -e "${GREEN}Word frequency analysis saved to $report${NC}" } generate_html_report() { local html_report="$REPORT_DIR/report.html" cat > "$html_report" << EOF <!DOCTYPE html> <html> <head> <title>Log Analysis Report</title> <style> body { font-family: Arial, sans-serif; margin: 20px; } h1 { color: #333; } h2 { color: #666; border-bottom: 1px solid #ccc; } .stats { background: #f5f5f5; padding: 10px; border-radius: 5px; } table { border-collapse: collapse; width: 100%; } th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; } th { background-color: #4CAF50; color: white; } tr:hover { background-color: #f5f5f5; } </style> </head> <body> <h1>Log Analysis Report</h1> <p>Generated on: $(date)</p> <p>Log file: $LOG_FILE</p> <h2>Basic Statistics</h2> <div class="stats"> EOF cat "$REPORT_DIR/basic_stats.txt" | while read line; do echo "<p>$line</p>" >> "$html_report" done cat >> "$html_report" << EOF </div> <h2>Top IP Addresses</h2> <table> <tr><th>Count</th><th>IP Address</th></tr> EOF grep -oE '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' "$LOG_FILE" | \ sort | uniq -c | sort -rn | head -$TOP_N | while read count ip; do echo "<tr><td>$count</td><td>$ip</td></tr>" >> "$html_report" done cat >> "$html_report" << EOF </table> <h2>Error Levels</h2> <table> <tr><th>Level</th><th>Count</th></tr> EOF for level in ERROR WARN INFO DEBUG FATAL; do count=$(grep -i "$level" "$LOG_FILE" | wc -l) echo "<tr><td>$level</td><td>$count</td></tr>" >> "$html_report" done cat >> "$html_report" << EOF </table> </body> </html> EOF echo -e "${GREEN}HTML report generated: $html_report${NC}" } # Main execution main() { clear echo "══════════════════════════════════════════" echo " LOG ANALYZER v1.0 " echo "══════════════════════════════════════════" echo "Analyzing: $LOG_FILE" echo create_report_dir analyze_basic_stats analyze_ip_addresses analyze_error_levels analyze_time_patterns analyze_frequent_words generate_html_report echo -e "\n${GREEN}Analysis complete! Reports available in $REPORT_DIR${NC}" if [ $VERBOSE -eq 1 ]; then echo -e "\n${YELLOW}Report summary:${NC}" ls -lh "$REPORT_DIR" fi } main

Conclusion

These exercises cover a wide range of Bash scripting concepts:

Key Skills Developed

  1. Basic Syntax: Variables, conditionals, loops
  2. File Operations: Reading, writing, manipulating files
  3. Text Processing: grep, sed, awk, pattern matching
  4. Data Structures: Arrays, associative arrays
  5. Functions: Modular code, recursion, scope
  6. Error Handling: Exit codes, validation, logging
  7. System Administration: User management, process monitoring
  8. Network Programming: Connectivity testing, port scanning
  9. Automation: Backup systems, log analyzers
  10. Best Practices: Error handling, documentation, modularity

Tips for Success

  1. Start simple: Begin with basic exercises and gradually increase complexity
  2. Practice regularly: Consistent practice is key to mastery
  3. Read other scripts: Analyze well-written scripts to learn patterns
  4. Use shellcheck: Validate your scripts with shellcheck.net
  5. Comment your code: Document complex logic for future reference
  6. Test edge cases: Always test with unexpected inputs
  7. Version control: Use git to track your script evolution

These exercises provide a solid foundation for becoming proficient in Bash scripting. Modify and expand them to suit your specific needs and challenges.

Complete Bash Exercises with Solutions

Practice Bash scripting with solved exercises to improve your command-line and scripting skills.
Link: https://macronepal.com/complete-bash-exercises-with-solutions/

Complete Guide to Bash Crontab Command

Learn how to schedule and automate tasks efficiently using the Bash crontab command.
Link: https://macronepal.com/complete-guide-to-bash-crontab-command-schedule-tasks/

Complete Guide to Bash Arrays

Understand how to create, manage, and use arrays in Bash scripting.
Link: https://macronepal.com/complete-guide-to-bash-arrays/

Bash Quiz: Test Your Knowledge

Test your Bash scripting knowledge with quizzes and practical questions.
Link: https://macronepal.com/bash-quiz-test-your-knowledge/

Complete Guide to Bash Functions

Learn how to write reusable Bash functions for cleaner and efficient scripts.
Link: https://macronepal.com/complete-guide-to-bash-functions/

Complete Guide to Bash Loops

Master loops in Bash for repetitive tasks and automation.
Link: https://macronepal.com/complete-guide-to-bash-loops/

Complete Guide to Bash If-Else Statements

Learn decision-making in Bash using conditional statements.
Link: https://macronepal.com/complete-guide-to-bash-ifelse-statements/

Complete Guide to Bash Operators

Explore arithmetic, logical, and comparison operators in Bash.
Link: https://macronepal.com/complete-guide-to-bash-operators/

Complete Guide to Bash Data Types

Understand variables and different data types used in Bash scripting.
Link: https://macronepal.com/complete-guide-to-bash-data-types/

Complete Guide to Bash Variables

Learn how to declare, assign, and use variables in Bash scripts.
Link: https://macronepal.com/complete-guide-to-bash-variables/

Complete Guide to Bash Scripting

A complete introduction to writing Bash scripts for automation.
Link: https://macronepal.com/complete-guide-to-bash-scripting/

Complete Guide to Basic Bash Syntax

Learn the fundamental syntax needed to write Bash commands and scripts.
Link: https://macronepal.com/complete-guide-to-basic-bash-syntax/

Complete Guide to Bash Chgrp Command

Learn how to change group ownership of files and directories.
Link: https://macronepal.com/complete-guide-to-bash-chgrp-command-change-group-ownership-2/

Complete Guide to Bash Chgrp Command (Alternate)

Another detailed explanation of Bash group ownership management.
Link: https://macronepal.com/complete-guide-to-bash-chgrp-command-change-group-ownership/

Complete Guide to Bash File Permissions and Ownership

Understand Linux file permissions and ownership management.
Link: https://macronepal.com/complete-guide-to-bash-file-permissions-and-ownership/

Complete Guide to Bash Chmod Command

Learn how to modify file permissions using chmod.
Link: https://macronepal.com/complete-guide-to-bash-chmod-command-change-file-permissions/

Complete Guide to Bash Chown Command

Change file ownership using the Bash chown command.
Link: https://macronepal.com/complete-guide-to-bash-chown-command-change-file-ownership/

Complete Guide to Bash SCP Command

Securely transfer files between systems using SCP.
Link: https://macronepal.com/complete-guide-to-bash-scp-command-secure-copy/

Complete Guide to Bash SSH Command

Learn secure remote access and server management using SSH.
Link: https://macronepal.com/complete-guide-to-bash-ssh-command/

Bash Wget Command Complete Guide

Download files from the internet using the wget command.
Link: https://macronepal.com/bash-wget-command-complete-guide-to-non-interactive-network-downloader/

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper