Capstone: A Real-World Backup/Cleanup Script
In this page:
Setting Up the Script Header and Logging
The capstone script starts with strict mode and a small timestamped logger, both covered earlier in this course, establishing the safety net the rest of the script relies on.
Example: Setting Up the Script Header and Logging
#!/bin/bash
set -euo pipefail
log() {
echo "[$(date +'%H:%M:%S')] $1"
}
log "backup script starting"
Login to try C/C++/Java/PHP code in the editor
Validating Preconditions Before Backing Up
Before doing anything, the script confirms the source directory exists and creates the backup destination idempotently with mkdir -p, so it fails fast and clearly if something is missing rather than partway through.
Example: Validating Preconditions Before Backing Up
#!/bin/bash
set -euo pipefail
source_dir="source_data"
backup_dir="backups"
mkdir -p "$source_dir"
echo "sample" > "$source_dir/file1.txt"
if [[ ! -d $source_dir ]]; then
echo "source directory missing, aborting" >&2
exit 1
fi
mkdir -p "$backup_dir"
echo "preconditions satisfied: source and backup directories are ready"
rm -rf "$source_dir" "$backup_dir"
Login to try C/C++/Java/PHP code in the editor
The Full Backup and Cleanup Script
This ties several concepts together: a main() entry point, a logging function, precondition checks, a timestamped backup archive, and cleanup of backups older than a retention count, all wrapped in strict mode with a clear final exit status.
Example: The Full Backup and Cleanup Script
#!/bin/bash
set -euo pipefail
source_dir="source_data"
backup_dir="backups"
retain_count=3
log() {
echo "[$(date +'%H:%M:%S')] $1"
}
validate() {
if [[ ! -d $source_dir ]]; then
log "ERROR: source directory '$source_dir' does not exist"
exit 1
fi
mkdir -p "$backup_dir"
}
run_backup() {
local stamp
stamp=$(date +'%Y%m%d_%H%M%S')
local archive="$backup_dir/backup_$stamp.tar"
tar -cf "$archive" -C "$source_dir" .
log "created backup: $archive"
}
cleanup_old_backups() {
local count
count=$(find "$backup_dir" -name 'backup_*.tar' | wc -l)
if (( count > retain_count )); then
find "$backup_dir" -name 'backup_*.tar' | sort | head -n "$((count - retain_count))" | xargs rm -f
log "pruned old backups, kept the most recent $retain_count"
else
log "no pruning needed, $count backup(s) present"
fi
}
main() {
mkdir -p "$source_dir"
echo "data" > "$source_dir/file.txt"
validate
run_backup
sleep 1
run_backup
cleanup_old_backups
log "backup script finished successfully"
rm -rf "$source_dir" "$backup_dir"
}
main "$@"
Login to try C/C++/Java/PHP code in the editor
- Writing a backup script without checking that the source directory actually exists first, which can produce a confusing empty backup.
- Deleting old backups without first confirming the new backup succeeded, risking losing all copies of the data at once.
- Skipping logging in a script meant to run unattended (e.g. via cron), making failures impossible to diagnose after the fact.
- A well-built real-world script combines strict mode, functions with a main() entry point, logging, validation, and idempotent operations.
- Validating inputs and preconditions before doing anything destructive prevents a whole class of avoidable failures.
- Structuring backup/cleanup logic as small functions (backup, then cleanup) keeps each responsibility easy to reason about independently.
- Even a small script benefits from the same discipline as a large one: check inputs, log progress, handle errors, and exit with a meaningful status.
Chapter Quiz — Complete all 7 topics to unlock
0/7 topics done
Complete these topics first:
- Quoting Pitfalls and Why to Always Quote Variables
- set -euo pipefail as a Script Header
- Structuring a Script (Functions, main() Pattern, Exit at the End)
- Idempotent Scripts (Safe to Re-Run)
- Logging in Scripts (Timestamped log Function)
- Writing Portable Scripts (Bash-isms vs POSIX sh)
- Capstone: A Real-World Backup/Cleanup Script