Skip to main content

Exercises and projects


Table of contents

  1. Recap quiz
  2. Guided exercises
  3. Mini-projects
  4. Final project
  5. Additional resources


1 - Recap quiz

Variables and expansion

Q1. How do you assign a default value if the variable is empty?

Answer

${var:-default} uses default if var is empty or unset. ${var:=default} does the same AND assigns default to var.

Q2. How do you get the filename without the path?

Answer

${path##*/} or basename "$path"

Q3. How do you get the extension of a file?

Answer

${filename##*.}

Conditions and loops

Q4. What is the difference between [ ] and [[ ]]?

Answer

[[ ]] is Bash-specific with:

  • Pattern matching (== with *, ?)
  • Regex (=~)
  • No need to escape <, >
  • Built-in && and ||

Q5. How do you read a file line by line while preserving spaces?

Answer
while IFS= read -r line; do
echo "$line"
done < fichier.txt

Functions and errors

Q6. How do you return a value from a function?

Answer

Via echo (capture with $()), or return N for a code (0-255).

Q7. What does set -euo pipefail do?

Answer
  • -e: Exit on the first error
  • -u: Error if a variable is undefined
  • -o pipefail: Error if any command in the pipe fails

Regex and text

Q8. How do you extract all email addresses from a file?

Answer
grep -oE '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' fichier.txt

Q9. How do you replace all spaces with underscores?

Answer

tr ' ' '_' or sed 's/ /_/g'

Q10. How do you compute the sum of a column with awk?

Answer

awk '{sum += $3} END {print sum}' fichier.txt

🔝 Back to table of contents



2 - Guided exercises

Exercise 1: Configuration validator

Create a script that validates a configuration file.

Specifications:

  • Checks that the file exists and is readable
  • Checks that the required keys are present
  • Displays validation errors
Solution
#!/bin/bash
set -euo pipefail

readonly CONFIG_FILE="${1:?Usage: $0 <config_file>}"
readonly REQUIRED_KEYS=("host" "port" "database")

validate_config() {
local file="$1"
local errors=0

# Vérifier existence
if [ ! -f "$file" ]; then
echo "ERREUR: Fichier non trouvé: $file"
return 1
fi

# Vérifier clés obligatoires
for key in "${REQUIRED_KEYS[@]}"; do
if ! grep -qE "^${key}=" "$file"; then
echo "ERREUR: Clé manquante: $key"
((errors++))
fi
done

# Vérifier format des valeurs
while IFS='=' read -r key value; do
[[ "$key" =~ ^#.*$ ]] && continue # Ignorer commentaires
[[ -z "$key" ]] && continue # Ignorer lignes vides

if [ -z "$value" ]; then
echo "WARN: Valeur vide pour: $key"
fi
done < "$file"

if [ $errors -gt 0 ]; then
echo "Validation échouée avec $errors erreur(s)"
return 1
fi

echo "Configuration valide!"
return 0
}

validate_config "$CONFIG_FILE"

Exercise 2: Process monitor

Create a script that monitors a process and restarts it if it goes down.

Solution
#!/bin/bash
set -uo pipefail

readonly PROCESS_NAME="${1:?Usage: $0 <process_name>}"
readonly CHECK_INTERVAL=5
readonly MAX_RESTARTS=3
readonly LOG_FILE="/var/log/monitor_${PROCESS_NAME}.log"

log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}

is_running() {
pgrep -x "$PROCESS_NAME" > /dev/null
}

restart_process() {
log "Tentative de redémarrage de $PROCESS_NAME"
systemctl restart "$PROCESS_NAME" 2>/dev/null || {
log "Échec du redémarrage via systemctl"
return 1
}
return 0
}

main() {
local restarts=0

log "Démarrage de la surveillance de $PROCESS_NAME"

while true; do
if ! is_running; then
log "ALERT: $PROCESS_NAME n'est pas en cours d'exécution!"

if [ $restarts -ge $MAX_RESTARTS ]; then
log "CRITICAL: Max restarts atteint ($MAX_RESTARTS)"
exit 1
fi

if restart_process; then
((restarts++))
log "Redémarrage réussi ($restarts/$MAX_RESTARTS)"
fi
else
restarts=0 # Reset si le process est OK
fi

sleep $CHECK_INTERVAL
done
}

main

Exercise 3: Log analyzer

Create a script that analyzes an access.log file and generates a report.

Solution
#!/bin/bash
set -euo pipefail

readonly LOG_FILE="${1:-/var/log/nginx/access.log}"

analyze_log() {
local log="$1"

[ -f "$log" ] || { echo "Fichier non trouvé: $log"; exit 1; }

echo "=== Analyse de $log ==="
echo ""

echo "--- Statistiques générales ---"
local total=$(wc -l < "$log")
echo "Total requêtes: $total"

echo ""
echo "--- Top 10 IPs ---"
awk '{print $1}' "$log" | sort | uniq -c | sort -rn | head -10

echo ""
echo "--- Codes HTTP ---"
awk '{print $9}' "$log" | sort | uniq -c | sort -rn

echo ""
echo "--- Top 10 URLs ---"
awk '{print $7}' "$log" | sort | uniq -c | sort -rn | head -10

echo ""
echo "--- Erreurs 5xx ---"
local errors=$(awk '$9 ~ /^5/ {count++} END {print count+0}' "$log")
echo "Total erreurs 5xx: $errors"

if [ "$errors" -gt 0 ]; then
echo "Détail:"
awk '$9 ~ /^5/ {print $1, $7, $9}' "$log" | head -10
fi
}

analyze_log "$LOG_FILE"

🔝 Back to table of contents



3 - Mini-projects

Project 1: Automated backup system

Build a complete backup system with:

  • Backup of files and database
  • Backup rotation (keep the last N)
  • Notification on error
  • Restore

Project 2: Deployment tool

Build a deployment tool with:

  • Backup before deployment
  • Deployment from Git
  • Health check after deployment
  • Automatic rollback on failure

Project 3: Monitoring dashboard

Create a script that generates an HTML report with:

  • CPU/RAM/Disk usage
  • Service status
  • Latest log errors
  • Charts (with gnuplot or similar)

🔝 Back to table of contents



4 - Final project: Complete DevOps tool

Specifications

Build a command-line tool devops-tool with the following features:

devops-tool <commande> [options]

Commandes:
deploy Déployer l'application
backup Sauvegarder les données
restore Restaurer depuis un backup
status Afficher l'état du système
logs Analyser les logs
clean Nettoyer les fichiers temporaires

Suggested structure

devops-tool/
├── bin/
│ └── devops-tool # Point d'entrée
├── lib/
│ ├── common.sh # Fonctions communes
│ ├── deploy.sh # Module déploiement
│ ├── backup.sh # Module backup
│ ├── monitor.sh # Module monitoring
│ └── logs.sh # Module logs
├── conf/
│ └── config.sh # Configuration
└── README.md

Entry point

#!/bin/bash
# bin/devops-tool

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LIB_DIR="$SCRIPT_DIR/../lib"

# Charger les modules
source "$LIB_DIR/common.sh"
source "$SCRIPT_DIR/../conf/config.sh"

usage() {
cat << EOF
Usage: $(basename "$0") <commande> [options]

Commandes:
deploy Déployer l'application
backup Sauvegarder les données
restore Restaurer un backup
status État du système
logs Analyser les logs
clean Nettoyer

Options:
-h, --help Afficher cette aide
-v Mode verbeux
EOF
}

main() {
[ $# -eq 0 ] && { usage; exit 1; }

local command="$1"
shift

case "$command" in
deploy)
source "$LIB_DIR/deploy.sh"
do_deploy "$@"
;;
backup)
source "$LIB_DIR/backup.sh"
do_backup "$@"
;;
restore)
source "$LIB_DIR/backup.sh"
do_restore "$@"
;;
status)
source "$LIB_DIR/monitor.sh"
show_status "$@"
;;
logs)
source "$LIB_DIR/logs.sh"
analyze_logs "$@"
;;
clean)
do_cleanup "$@"
;;
-h|--help)
usage
exit 0
;;
*)
echo "Commande inconnue: $command"
usage
exit 1
;;
esac
}

main "$@"

🔝 Back to table of contents



5 - Additional resources

Documentation

Tools

  • ShellCheck: Linter for Bash
  • bashdb: Debugger
  • shfmt: Code formatter

Best practices

Practice

🔝 Back to table of contents



Congratulations!

You have completed the Linux 4 - Bash Scripting course! 🎉

You now know how to:

  • ✅ Variables, conditions, loops
  • ✅ Functions and modularity
  • ✅ Text manipulation (sed, awk, regex)
  • ✅ Error handling and debugging
  • ✅ Best practices and security
  • ✅ Professional DevOps scripts

Next steps

  1. Practice with real-world projects
  2. Contribute to open source projects
  3. Continue with Linux 5 - Advanced administration

← Back to the course table of contents