====== idtm-backup1.bash ====== You can download this backup script and unzip it. {{ :docs:idtm-backup1.bash.zip |}} #!/bin/bash # http://www.noah.org/engineering/src/shell/rsync_backup # https://digitalis.io/blog/technology/incremental-backups-with-rsync-and-hard-links/ # https://linuxconfig.org/how-to-create-incremental-backups-using-rsync-on-linux # https://www.perfacilis.com/blog/systeembeheer/linux/incremental-back-up-script-using-rsync.html # The source path to the folder we want to backup. SOURCE_DIR="/home/idtm/IDTM-Library" # The destination path to the external backup drive DESTINATION_BASE="/media/idtm/IDTM-Backup1/IDTM-Server/IDTM-Library" # Get the current date and time DATETIME="$(date '+%Y-%m-%d_%H%M%S')" # Set the path for today's backup BACKUP_PATH="${DESTINATION_BASE}/${DATETIME}" LATEST_LINK="${DESTINATION_BASE}/latest" # Set where weekly backups are stored DESTINATION_BASE_WEEKLY="/media/idtm/IDTM-Backup1/IDTM-Server/IDTM-Library/weekly" # Set where monthly backups are stored DESTINATION_BASE_MONTHLY="/media/idtm/IDTM-Backup1/IDTM-Server/IDTM-Library/monthly" PERMS_DIR=755 PERMS_FILE=644 NORMALIZE_PERMS=true # set minimum number of seconds between backups INTERVAL=3000 # default is 3000 (one hour = 3600 seconds) # set how many seconds to keep daily backups KEEP_DAILY_FOR_THIS_LONG=604800 # one week = 604800 seconds # create a "latest" directory if it doesn't exist prepare_backup_dir() { if [ ! -d $LATEST_LINK ]; then mkdir -p $LATEST_LINK touch -d "January 1 2000" $LATEST_LINK fi } # exit this script if not enough time has passed since last backup check_interval() { local LAST=$(stat -c %Y $LATEST_LINK) local NOW=$(date +%s) local ELAPSED=$(($NOW - $LAST)) if [ "$ELAPSED" -lt "$INTERVAL" ]; then echo "Last backup was ${ELAPSED}s ago, which is less than ${INTERVAL}s." exit fi } perform_backup() { rsync -av --delete --exclude="lost+found" \ "${SOURCE_DIR}/" \ --link-dest "${LATEST_LINK}" \ "${BACKUP_PATH}" rm -rf "${LATEST_LINK}" echo "ln -s ${BACKUP_PATH} ${LATEST_LINK}" ln -s "${BACKUP_PATH}" "${LATEST_LINK}" touch "${BACKUP_PATH}" # normalize permissions to reduce access problems later if [ "$NORMALIZE_PERMS" = true ]; then echo "normalize permissions" find $LATEST_LINK -type d -exec chmod $PERMS_DIR {} \; find $LATEST_LINK -type f -exec chmod $PERMS_FILE {} \; fi } verify_days() { local STAT_AGE=0 local NOW=$(date +%s) local ELAPSED=0 # loop through each backup folder for f in $DESTINATION_BASE/* ; do BASENAME="$(basename $f)" # check if it is a directory if [ -d "${f}" ]; then # exclude latest, weekly, and monthly if [[ "$BASENAME" != "latest" && "$BASENAME" != "weekly" && "$BASENAME" != "monthly" ]]; then # get age of the file in seconds STAT_AGE=$(stat -c %Y $f) ELAPSED=$(($NOW - $STAT_AGE)) echo "The age of $f is $ELAPSED seconds" # delete daily backups older than KEEP_DAILY_FOR_THIS_LONG seconds ago if [ "$ELAPSED" -ge "$KEEP_DAILY_FOR_THIS_LONG" ]; then rm -rf $f fi fi fi done } verify_weeks() { local NOW=$(date +%s) local WEEKS_SECS_ELAPSED=0 local F_AGE=0 # how many weeks to keep backups local WEEKS_LIMIT=6 local WEEKS_LIMIT_SECS=$(( 604800*$WEEKS_LIMIT )) # get the current week number of this year local THIS_WEEK="$(date +%U)"; # get the current year number of this year local THIS_YEAR="$(date +%y)"; # get the latest previous weekly backup local LATEST_WEEKLY="$(ls -tr $DESTINATION_BASE_WEEKLY | tail -1)" local AGE_LATEST_WEEKLY="$(stat -c %Y $DESTINATION_BASE_WEEKLY/$LATEST_WEEKLY)" local WEEK_NUMBER_LATEST_WEEKLY="$(date --date=@$AGE_LATEST_WEEKLY +%U)" local YEAR_NUMBER_LATEST_WEEKLY="$(date --date=@$AGE_LATEST_WEEKLY +%y)" # make another week backup if there isn't a backup for this week number yet if [ "$WEEK_NUMBER_LATEST_WEEKLY" -lt "$THIS_WEEK" ] || \ [ "$YEAR_NUMBER_LATEST_WEEKLY" -lt "$THIS_YEAR" ] || \ [ -z "$LATEST_WEEKLY" ] ; then cp -al "${BACKUP_PATH}" "$DESTINATION_BASE_WEEKLY/$DATETIME" echo "Weekly backup made: $DESTINATION_BASE_WEEKLY/$DATETIME" fi # clean up weeks older than WEEKS_OLD weeks for f in $DESTINATION_BASE_WEEKLY/* ; do BASENAME="$(basename $f)" # check if it is a directory if [ -d "${f}" ]; then F_AGE="$(stat -c %Y $f)" WEEKS_SECS_ELAPSED=$(($NOW - $F_AGE)) # delete weekly backups older than WEEKS_LIMIT weeks ago if [ $WEEKS_SECS_ELAPSED -ge $WEEKS_LIMIT_SECS ]; then rm -rf $f fi fi done } verify_months() { # get the current month number of this year local THIS_MONTH="$(date +%m)"; local THIS_YEAR="$(date +%y)"; # get the latest previous monthly backup local LATEST_MONTHLY="$(ls -tr $DESTINATION_BASE_MONTHLY | tail -1)" local AGE_LATEST_MONTHLY="$(stat -c %Y $DESTINATION_BASE_MONTHLY/$LATEST_MONTHLY)" local MONTH_NUMBER_LATEST_MONTHLY="$(date --date=@$AGE_LATEST_MONTHLY +%m)" local YEAR_NUMBER_LATEST_MONTHLY="$(date --date=@$AGE_LATEST_MONTHLY +%y)" # copy another month if there isn't a backup for this mmonth number yet if [ "$MONTH_NUMBER_LATEST_MONTHLY" -lt "$THIS_MONTH" ] || \ [ "$YEAR_NUMBER_LATEST_MONTHLY" -lt "$THIS_YEAR" ] || \ [ -z "$LATEST_MONTHLY" ] ; then cp -al "${BACKUP_PATH}" "$DESTINATION_BASE_MONTHLY/$DATETIME" echo "Monthly backup made: $DESTINATION_BASE_MONTHLY/$DATETIME" fi } main () { prepare_backup_dir check_interval perform_backup verify_days verify_weeks verify_months } main