backup-directory.sh
Niveau : Intermรฉdiaire
Backup de rรฉpertoires avec rotation et compression.
Description
Ce script effectue des sauvegardes de rรฉpertoires : - Compression tar.gz ou zip - Rotation automatique des anciennes sauvegardes - Exclusion de ficyesterdays/dossiers - Vรฉrification d'intรฉgritรฉ - Notification par email (optionnel)
Prรฉrequis
- Systรจme : Linux (RHEL/Debian)
- Permissions : Droits de lecture sur le rรฉpertoire source et d'รฉcriture sur le rรฉpertoire de destination
- Dรฉpendances :
tar,gzip(ouzip),bc
Cas d'Usage
- Sauvegarde quotidienne automatisรฉe : Exรฉcution planifiรฉe via cron pour backup rรฉgulier des donnรฉes critiques
- Archivage de projets : Compression et conservation de versions multiples avec rotation automatique
- Migration de donnรฉes : Sauvegarde complรจte avant migration de serveur avec vรฉrification d'intรฉgritรฉ
- Rรฉcupรฉration d'urgence : Maintien de backups testรฉs pour restauration rapide en cas de panne
Script
#!/bin/bash
#===============================================================================
# Script Name: backup-directory.sh
# Description: Directory backup with rotation
# Author: ShellBook
# Version: 1.0
#===============================================================================
set -euo pipefail
# Colors
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m'
# Default configuration
COMPRESS_FORMAT="tar.gz"
KEEP_BACKUPS=7
DATE_FORMAT="%Y%m%d_%H%M%S"
VERIFY=false
EXCLUDE_FILE=""
VERBOSE=false
usage() {
cat << EOF
Usage: $(basename "$0") [OPTIONS] SOURCE DESTINATION
Backup de rรฉpertoires avec rotation.
Arguments:
SOURCE Source directory ร sauvegarder
DESTINATION Rรฉpertoire de destination des backups
Options:
-f, --format FORMAT Format de compression (tar.gz, zip) (default: tar.gz)
-k, --keep NUM Nombre de backups ร conserver (default: 7)
-e, --exclude FILE Ficyesterday contenant les exclusions
-v, --verify Vรฉrifier l'intรฉgritรฉ aprรจs crรฉation
--verbose Verbose mode
-h, --help Show this help
Examples:
$(basename "$0") /home/user/data /backup/user
$(basename "$0") -k 14 -v /var/www /backup/www
$(basename "$0") -f zip -e exclude.txt /data /backup
EOF
}
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
}
format_size() {
local bytes=$1
if (( bytes >= 1073741824 )); then
echo "$(echo "scale=2; $bytes/1073741824" | bc)G"
elif (( bytes >= 1048576 )); then
echo "$(echo "scale=2; $bytes/1048576" | bc)M"
elif (( bytes >= 1024 )); then
echo "$(echo "scale=2; $bytes/1024" | bc)K"
else
echo "${bytes}B"
fi
}
create_backup_tar() {
local source=$1
local dest_file=$2
local exclude_opts=""
if [[ -n "$EXCLUDE_FILE" ]] && [[ -f "$EXCLUDE_FILE" ]]; then
exclude_opts="--exclude-from=$EXCLUDE_FILE"
fi
local tar_opts="-czf"
[[ "$VERBOSE" == "true" ]] && tar_opts="-czvf"
tar $tar_opts "$dest_file" $exclude_opts -C "$(dirname "$source")" "$(basename "$source")"
}
create_backup_zip() {
local source=$1
local dest_file=$2
local exclude_opts=""
if [[ -n "$EXCLUDE_FILE" ]] && [[ -f "$EXCLUDE_FILE" ]]; then
exclude_opts="-x@$EXCLUDE_FILE"
fi
local zip_opts="-r"
[[ "$VERBOSE" == "true" ]] && zip_opts="-rv"
(cd "$(dirname "$source")" && zip $zip_opts "$dest_file" "$(basename "$source")" $exclude_opts)
}
verify_backup() {
local backup_file=$1
log_info "Checking de l'intรฉgritรฉ..."
case "$COMPRESS_FORMAT" in
tar.gz)
if tar -tzf "$backup_file" &>/dev/null; then
log_info "Intรฉgritรฉ OK: $backup_file"
return 0
else
log_error "Intรฉgritรฉ FAIL: $backup_file"
return 1
fi
;;
zip)
if unzip -t "$backup_file" &>/dev/null; then
log_info "Intรฉgritรฉ OK: $backup_file"
return 0
else
log_error "Intรฉgritรฉ FAIL: $backup_file"
return 1
fi
;;
esac
}
rotate_backups() {
local dest_dir=$1
local source_name=$2
log_info "Rotation des backups (conservation: $KEEP_BACKUPS)..."
local pattern="${source_name}_*.${COMPRESS_FORMAT}"
local backups
backups=$(ls -t "$dest_dir"/$pattern 2>/dev/null || true)
local count=0
for backup in $backups; do
count=$((count + 1))
if (( count > KEEP_BACKUPS )); then
log_info "Deleting: $(basename "$backup")"
rm -f "$backup"
fi
done
}
main() {
local source=""
local destination=""
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-f|--format)
COMPRESS_FORMAT="$2"
shift 2
;;
-k|--keep)
KEEP_BACKUPS="$2"
shift 2
;;
-e|--exclude)
EXCLUDE_FILE="$2"
shift 2
;;
-v|--verify)
VERIFY=true
shift
;;
--verbose)
VERBOSE=true
shift
;;
-h|--help)
usage
exit 0
;;
-*)
log_error "Unknown option: $1"
usage
exit 1
;;
*)
if [[ -z "$source" ]]; then
source="$1"
elif [[ -z "$destination" ]]; then
destination="$1"
fi
shift
;;
esac
done
# Validation
if [[ -z "$source" ]] || [[ -z "$destination" ]]; then
log_error "Source et destination requises"
usage
exit 1
fi
if [[ ! -d "$source" ]]; then
log_error "Source does not exist: $source"
exit 1
fi
if [[ ! -d "$destination" ]]; then
log_info "Creating du rรฉpertoire destination: $destination"
mkdir -p "$destination"
fi
if [[ "$COMPRESS_FORMAT" != "tar.gz" ]] && [[ "$COMPRESS_FORMAT" != "zip" ]]; then
log_error "Format non supportรฉ: $COMPRESS_FORMAT"
exit 1
fi
# Nom du backup
local source_name=$(basename "$source")
local timestamp=$(date +"$DATE_FORMAT")
local backup_name="${source_name}_${timestamp}.${COMPRESS_FORMAT}"
local backup_path="${destination}/${backup_name}"
echo -e "${GREEN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
echo -e "${GREEN} BACKUP: $source_name${NC}"
echo -e "${GREEN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
log_info "Source: $source"
log_info "Destination: $backup_path"
log_info "Format: $COMPRESS_FORMAT"
# Calcul taille source
local source_size=$(du -sb "$source" | awk '{print $1}')
log_info "Taille source: $(format_size $source_size)"
# Crรฉation du backup
log_info "Creating du backup en cours..."
local start_time=$(date +%s)
case "$COMPRESS_FORMAT" in
tar.gz)
create_backup_tar "$source" "$backup_path"
;;
zip)
create_backup_zip "$source" "$backup_path"
;;
esac
local end_time=$(date +%s)
local duration=$((end_time - start_time))
# Stats du backup
local backup_size=$(stat -f%z "$backup_path" 2>/dev/null || stat -c%s "$backup_path")
local ratio=$(echo "scale=1; $backup_size * 100 / $source_size" | bc)
log_info "Backup crรฉรฉ: $backup_name"
log_info "Taille: $(format_size $backup_size) (${ratio}% de l'original)"
log_info "Durรฉe: ${duration}s"
# Vรฉrification
if [[ "$VERIFY" == "true" ]]; then
verify_backup "$backup_path" || exit 1
fi
# Rotation
rotate_backups "$destination" "$source_name"
# Rรฉsumรฉ
echo -e "${GREEN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
log_info "Backup completed avec succรจs!"
# Liste des backups existants
echo ""
log_info "Backups disponibles:"
ls -lh "$destination"/${source_name}_*.${COMPRESS_FORMAT} 2>/dev/null | \
awk '{print " " $NF " (" $5 ")"}'
}
main "$@"
Usage
# Rendre exรฉcutable
chmod +x backup-directory.sh
# Backup simple
./backup-directory.sh /home/user/documents /backup
# Conserver 14 backups
./backup-directory.sh -k 14 /var/www /backup/www
# Format ZIP avec vรฉrification
./backup-directory.sh -f zip -v /data /backup
# Avec exclusions
echo "*.log" > exclude.txt
echo "cache/" >> exclude.txt
./backup-directory.sh -e exclude.txt /app /backup
Sortie Exemple
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
BACKUP: documents
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[INFO] Source: /home/user/documents
[INFO] Destination: /backup/documents_20240115_143022.tar.gz
[INFO] Format: tar.gz
[INFO] Taille source: 1.5G
[INFO] Crรฉation du backup en cours...
[INFO] Backup crรฉรฉ: documents_20240115_143022.tar.gz
[INFO] Taille: 485M (32.3% de l'original)
[INFO] Durรฉe: 45s
[INFO] Vรฉrification de l'intรฉgritรฉ...
[INFO] Intรฉgritรฉ OK: /backup/documents_20240115_143022.tar.gz
[INFO] Rotation des backups (conservation: 7)...
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[INFO] Backup completed avec succรจs!
[INFO] Backups disponibles:
/backup/documents_20240115_143022.tar.gz (485M)
/backup/documents_20240114_143022.tar.gz (482M)
/backup/documents_20240113_143022.tar.gz (480M)
Ficyesterday d'Exclusion
Exemple de ficyesterday exclude.txt:
# Ficyesterdays temporaires
*.tmp
*.temp
*.swp
*~
# Logs
*.log
logs/
# Caches
cache/
.cache/
__pycache__/
node_modules/
# Ficyesterdays systรจme
.DS_Store
Thumbs.db
Automatisation Cron
# Backup quotidien ร 2h du matin
0 2 * * * /opt/scripts/backup-directory.sh -v /var/www /backup/www >> /var/log/backup.log 2>&1
# Backup hebdomadaire avec rotation 4 semaines
0 3 * * 0 /opt/scripts/backup-directory.sh -k 4 /home /backup/home >> /var/log/backup.log 2>&1