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
- Basic Exercises
- Variables and Data Types
- Conditional Statements
- Loop Exercises
- Function Exercises
- File Operations
- Text Processing
- Arrays and Data Structures
- Advanced Scripting
- System Administration
- 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
- Basic Syntax: Variables, conditionals, loops
- File Operations: Reading, writing, manipulating files
- Text Processing: grep, sed, awk, pattern matching
- Data Structures: Arrays, associative arrays
- Functions: Modular code, recursion, scope
- Error Handling: Exit codes, validation, logging
- System Administration: User management, process monitoring
- Network Programming: Connectivity testing, port scanning
- Automation: Backup systems, log analyzers
- Best Practices: Error handling, documentation, modularity
Tips for Success
- Start simple: Begin with basic exercises and gradually increase complexity
- Practice regularly: Consistent practice is key to mastery
- Read other scripts: Analyze well-written scripts to learn patterns
- Use shellcheck: Validate your scripts with shellcheck.net
- Comment your code: Document complex logic for future reference
- Test edge cases: Always test with unexpected inputs
- 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/
