Last active
March 21, 2025 10:36
-
-
Save tflori/86afd8016454a95869eb7ab5a002ad53 to your computer and use it in GitHub Desktop.
Backup Sript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Backup Duration: How old a backup can be before a new one is triggered (in days) | |
BACKUP_DURATION=5 | |
# Base backup location (will create a folder named by hostname) | |
BACKUP_LOCATION="/mnt/backups/$(hostname)" | |
# Backup sources (identifier and path) | |
BACKUP_BACKUPS=( | |
"myhome $HOME" | |
# "identifier" "/path/to/other/folder" | |
) | |
# Folders to exclude from backup | |
BACKUP_EXCLUDES=( | |
"myhome /Downloads" | |
"myhome /.cache" | |
# "identifier" "exclude pattern" | |
) | |
# Backup options for each source (incremental, keep, or versioned) | |
BACKUP_OPTIONS=( | |
"myhome incremental" | |
# "identifier" "space separated options" | |
) | |
# Compress specific paths in the backup (not incremental or versioned) | |
BACKUP_COMPRESSION=( | |
"myhome .config/vivaldi" | |
# "identifier" "relative/path" | |
) | |
# RAID metadata to backup (list of md devices, e.g., "md0") | |
BACKUP_RAID_META=( | |
# "md0" | |
) | |
# Commands to mount necessary drives before backup (optional) | |
BACKUP_MOUNT_BEFORE=( | |
# "mount -t ntfs /dev/sdX /mnt" | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Load the config file | |
source ~/.config/backup.conf.sh | |
# Default values | |
FORCE_BACKUP=false | |
DRY_RUN=false | |
VERBOSE=false | |
LIST_BACKUPS=false | |
SPECIFIC_BACKUP="" | |
# Function to display usage | |
usage() { | |
echo "Usage: rbackup [options] [<backup>]" | |
echo " <backup> Backup only this part (optional; will not update timestamp)" | |
echo "Options:" | |
echo " --force, -f Ignore backup timestamp and don't ask" | |
echo " --dry-run, -n Simulate backup without copying files" | |
echo " --verbose, -v Show detailed output" | |
echo " --list, -l List available backups" | |
echo " --help, -h Show this help message" | |
exit 0 | |
} | |
# Parse command-line arguments | |
while [[ $# -gt 0 ]]; do | |
case "$1" in | |
--force|-f) | |
FORCE_BACKUP=true | |
;; | |
--dry-run|-n) | |
DRY_RUN=true | |
;; | |
--verbose|-v) | |
VERBOSE=true | |
;; | |
--list|-l) | |
LIST_BACKUPS=true | |
;; | |
--help|-h) | |
usage | |
;; | |
*) | |
SPECIFIC_BACKUP="$1" | |
;; | |
esac | |
shift | |
done | |
# If --list is used, show available backups and exit | |
if [ "$LIST_BACKUPS" = true ]; then | |
echo "Available backups:" | |
for ITEM in "${BACKUP_BACKUPS[@]}"; do | |
IDENTIFIER=$(echo "$ITEM" | /usr/bin/cut -d ' ' -f 1) | |
echo " - $IDENTIFIER" | |
done | |
exit 0 | |
fi | |
# Define the backup file to check the last backup date | |
BACKUP_FILE="$BACKUP_LOCATION/.backup" | |
# Check if the last backup exists and is not too old, unless forced | |
if [ "$FORCE_BACKUP" = false ] && [ -f "$BACKUP_FILE" ]; then | |
LAST_BACKUP=$(cat "$BACKUP_FILE") | |
LAST_BACKUP_DATE=$(/usr/bin/date -d "$LAST_BACKUP" +%s) | |
CURRENT_DATE=$(/usr/bin/date +%s) | |
DAYS_SINCE_LAST_BACKUP=$(( (CURRENT_DATE - LAST_BACKUP_DATE) / 86400 )) | |
if [ "$DAYS_SINCE_LAST_BACKUP" -lt "$BACKUP_DURATION" ]; then | |
echo "Last backup was $DAYS_SINCE_LAST_BACKUP days ago. No backup needed." | |
exit 0 | |
fi | |
fi | |
# Ask the user if they want to start a backup now (unless forced) | |
if [ "$FORCE_BACKUP" = false ]; then | |
if ! zenity --question --title="Backup" --text="Do you want to start a backup now?" --default-cancel; then | |
echo "Backup cancelled by user." | |
exit 1 | |
fi | |
fi | |
# Mount necessary drives (optional) | |
for MOUNT_CMD in "${BACKUP_MOUNT_BEFORE[@]}"; do | |
eval "$MOUNT_CMD" | |
done | |
# Backup RAID metadata if necessary | |
for RAID in "${BACKUP_RAID_META[@]}"; do | |
if [ -e "/dev/$RAID" ]; then | |
echo "Backing up RAID metadata: $RAID" | |
dd if="/dev/$RAID" of="$BACKUP_LOCATION/$(hostname)_$RAID.$(/usr/bin/date +%F).bin" bs=1M | |
fi | |
done | |
# Loop through each backup source | |
for ITEM in "${BACKUP_BACKUPS[@]}"; do | |
IDENTIFIER=$(echo "$ITEM" | /usr/bin/cut -d ' ' -f 1) | |
BACKUP_PATH=$(echo "$ITEM" | /usr/bin/cut -d ' ' -f 2) | |
# If a specific backup is requested, skip others | |
if [ -n "$SPECIFIC_BACKUP" ] && [ "$IDENTIFIER" != "$SPECIFIC_BACKUP" ]; then | |
continue | |
fi | |
# Check for exclusions | |
EXCLUDE_PATTERNS="" | |
for EXCLUDE in "${BACKUP_EXCLUDES[@]}"; do | |
EXCLUDE_ID=$(echo "$EXCLUDE" | /usr/bin/cut -d ' ' -f 1) | |
EXCLUDE_PATH=$(echo "$EXCLUDE" | /usr/bin/cut -d ' ' -f 2) | |
if [ "$EXCLUDE_ID" == "$IDENTIFIER" ]; then | |
EXCLUDE_PATTERNS="$EXCLUDE_PATTERNS --exclude=$EXCLUDE_PATH" | |
fi | |
done | |
# Check for compression config | |
for COMPRESSION_ITEM in "${BACKUP_COMPRESSION[@]}"; do | |
COMP_IDENTIFIER=$(echo "$COMPRESSION_ITEM" | /usr/bin/cut -d ' ' -f 1) | |
COMPRESSION_PATH=$(echo "$COMPRESSION_ITEM" | /usr/bin/cut -d ' ' -f 2) | |
if [ "$IDENTIFIER" == "$COMP_IDENTIFIER" ]; then | |
TAR_FILE="$BACKUP_LOCATION/$IDENTIFIER-$(basename "$COMPRESSION_PATH").tar.gz" | |
if [ "$DRY_RUN" = true ]; then | |
echo "[DRY RUN] Would compress $COMPRESSION_PATH to $TAR_FILE" | |
else | |
echo "Compressing $COMPRESSION_PATH for $IDENTIFIER" | |
# Check if pv is available for progress output | |
if command -v pv &>/dev/null; then | |
tar -cf - -C "$BACKUP_PATH" "$COMPRESSION_PATH" | pv -s $(du -sb "$BACKUP_PATH/$COMPRESSION_PATH" | awk '{print $1}') | gzip > $TAR_FILE | |
else | |
tar -czf $TAR_FILE -C "$BACKUP_PATH" "$COMPRESSION_PATH" | |
fi | |
fi | |
EXCLUDE_PATTERNS="$EXCLUDE_PATTERNS --exclude=$COMPRESSION_PATH" | |
fi | |
done | |
# Define rsync options | |
OPTIONS="" | |
for OPTION in "${BACKUP_OPTIONS[@]}"; do | |
if [[ "$OPTION" == "$IDENTIFIER" ]]; then | |
if [[ "$OPTION" == *"incremental"* ]]; then | |
OPTIONS="$OPTIONS --link-dest=$BACKUP_LOCATION/$IDENTIFIER/previous" | |
fi | |
if [[ "$OPTION" == *"versioned"* ]]; then | |
OPTIONS="$OPTIONS --backup --backup-dir=$BACKUP_LOCATION/$IDENTIFIER/versions" | |
fi | |
fi | |
done | |
# Perform the backup using rsync | |
echo "Backing up $IDENTIFIER from $BACKUP_PATH" | |
if [ "$DRY_RUN" = true ]; then | |
echo "[DRY RUN] Would execute: /usr/bin/rsync -avh --progress $EXCLUDE_PATTERNS $OPTIONS \"$BACKUP_PATH/\" \"$BACKUP_LOCATION/$IDENTIFIER/\"" | |
else | |
/usr/bin/rsync -ah --info=progress2 $EXCLUDE_PATTERNS $OPTIONS "$BACKUP_PATH/" "$BACKUP_LOCATION/$IDENTIFIER/" | |
fi | |
done | |
# Update the last backup date unless a specific backup was run or it's a dry run | |
if [ "$DRY_RUN" = false ] && [ -z "$SPECIFIC_BACKUP" ]; then | |
/usr/bin/date "+%FT%T" > "$BACKUP_FILE" | |
fi | |
echo "Backup completed!" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment