#!/bin/bash # rsync_magic.sh - A smart, feature-rich rsync wrapper for safe and powerful file synchronization. set -euo pipefail # ======= Configuration ======= LOG_FILE="/var/log/rsync_magic.log" EXCLUDES="/etc/rsync_magic_excludes.txt" TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") # ======= Help Function ======= usage() { echo "Usage: $0 [--dry-run] " echo "Optional: --dry-run to simulate the sync" exit 1 } # ======= Argument Parsing ======= DRY_RUN=0 if [[ "${1:-}" == "--dry-run" ]]; then DRY_RUN=1 shift fi SOURCE="${1:-}" DEST="${2:-}" if [[ -z "$SOURCE" || -z "$DEST" ]]; then usage fi if [[ ! -d "$SOURCE" ]]; then echo "Error: Source directory '$SOURCE' not found!" exit 2 fi mkdir -p "$DEST" # ======= Construct Rsync Command ======= RSYNC_OPTS=( -a # archive mode (recursive, preserve symlinks, perms, times, groups, etc.) -v # verbose -z # compress during transfer -h # human-readable output -u # skip files that are newer on the receiver -P # show progress during transfer and allow partial transfer resume -c # compare files using checksum -x # don't cross filesystem boundaries -A # preserve ACLs -X # preserve extended attributes --delete # delete extraneous files from destination --numeric-ids # don't map uid/gid numbers to usernames --inplace # update destination files in place --backup # backup overwritten files --backup-dir="${DEST}/.backup-${TIMESTAMP}" # backup location ) # Add dry-run flag if needed if [[ "$DRY_RUN" -eq 1 ]]; then RSYNC_OPTS+=(--dry-run) echo "Running in DRY RUN mode..." fi # Add exclude file if it exists if [[ -f "$EXCLUDES" ]]; then RSYNC_OPTS+=(--exclude-from="$EXCLUDES") fi # ======= Run Rsync ======= echo "Starting rsync at $TIMESTAMP" | tee -a "$LOG_FILE" echo "Source: $SOURCE" | tee -a "$LOG_FILE" echo "Destination: $DEST" | tee -a "$LOG_FILE" rsync "${RSYNC_OPTS[@]}" "$SOURCE/" "$DEST/" 2>&1 | tee -a "$LOG_FILE" echo "rsync completed at $(date +"%Y-%m-%d_%H-%M-%S")" | tee -a "$LOG_FILE"