Exercises and projects
Table of contents
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
- Practice with real-world projects
- Contribute to open source projects
- Continue with Linux 5 - Advanced administration