23 Commits

Author SHA1 Message Date
Laurent Cozic cca0c1ac56 Removed test code 2013-11-15 15:34:17 +08:00
Laurent Cozic 78003f6b94 Support for Mac OS X 2013-11-15 14:59:59 +08:00
Robert Bruce Park 324a3ffc9a Whitespace fix, oops. 2013-11-14 19:36:32 -08:00
Robert Bruce Park 12deac25d6 Correctly handle most recent backup if it's older than 24hs.
Code was assuming that you'd run backups hourly, eg, so you'd have
many backups within the last 24 hours. Code assumed that by the time
you found a backup more than 24h old, you'd already have a value for
$prev set. In the event that your most recent backup is more than 24
hours old, $prev would not be set and bash would get an error "unary
operator expected" when it tried to compare the day of the newest
backup to the empty $prev value.

I fix this by setting a default value for $prev, one that cannot be
mistaken for an existing backup because it does not represent a valid
date. This ensures that the most recent backup is correctly preserved
regardless of it's age.
2013-11-14 19:31:22 -08:00
Robert Bruce Park 855c77a83f Simplify fn_is_backup_destination implementation. 2013-11-14 19:03:26 -08:00
Robert Bruce Park 4250423d98 Make fn_expire_backup more robust. 2013-11-14 18:50:34 -08:00
Robert Bruce Park 584e3a9b4b Drop BACKUP_FOLDER_PATTERN since it's now only used in one place. 2013-11-13 10:53:13 -08:00
Robert Bruce Park 19446c24b9 Take the training wheels off ;-) 2013-11-13 10:34:47 -08:00
Robert Bruce Park 8bb5b3f265 Rearrange function definitions. 2013-11-13 10:21:47 -08:00
Robert Bruce Park a67efdd0f1 Implement fn_expire_backups to reduce code duplication. 2013-11-13 10:17:41 -08:00
Robert Bruce Park 32e71ce9aa Implement fn_find_backups to reduce code duplication. 2013-11-13 10:16:27 -08:00
Robert Bruce Park 72df5bf33e Rebase on master. 2013-11-13 09:16:31 -08:00
Robert Bruce Park fe0bfde041 More robust path handling. 2013-11-13 09:12:50 -08:00
Laurent 8a2e91813b Update README.md
Now done: "Check if there's enough space in the destination before doing the backup. Also automatically delete old backups."
2013-11-13 17:57:51 +08:00
Laurent Cozic c505ecbfb8 Merge branch 'master' of https://github.com/rastkov/rsync-time-backup into rastkov-master
Conflicts:
	rsync_tmbackup.sh
2013-11-13 17:51:15 +08:00
Laurent Cozic 159d8b6416 Merge branch 'master' of https://github.com/laurent22/rsync-time-backup
Conflicts:
	rsync_tmbackup.sh
2013-11-13 17:47:58 +08:00
Laurent Cozic 2fd6b109f0 Using find instead of ls to find the last and second last directory used for backup. 2013-11-13 17:46:20 +08:00
Robert Bruce Park d425a05528 Initial implementation of Time Machine style backup pruning.
Within 24 hours, all backups are kept. Within 1 month, only the most
recent backup for each day is kept. For all previous backups, only the
most recent of each month is kept.

This is not *quite* the same as Time Machine, but this implementation
is a lot easier to do since it is based on string comparisons of the
dates and doesn't require any "hard" date logic.

Also this commit just 'echo's what will be deleted, and does not
actually delete anything yet because I am still testing it.
2013-11-13 00:01:17 -08:00
Laurent Cozic c23f47164e Merge branch 'master' of https://github.com/tyriis/rsync-time-backup into tyriis-master
Changed regex for grep iirc POSIX mode

Conflicts:
	rsync_tmbackup.sh
2013-11-12 15:50:02 +08:00
rastkov 99258d80ab Update rsync_tmbackup.sh
Set LAST_TIME, LINE_CONUT and SECOND_LAST_TIME based on `uname -s` output for Mac OS compatibility.
2013-11-01 14:32:59 +01:00
rastkov 27a0a1881f Update rsync_tmbackup.sh
Using find with regex instead of glob
2013-11-01 08:48:21 +01:00
rastkov 8318db90c5 Update rsync_tmbackup.sh 2013-10-29 12:15:38 +01:00
rastkov a40a733902 Update rsync_tmbackup.sh
Using find instead of ls to find the last and second last directory used for backup.
2013-10-29 12:07:30 +01:00
2 changed files with 97 additions and 43 deletions
+1 -5
View File
@@ -40,14 +40,10 @@ An optional exclude file can be provided as a third parameter. It should be comp
# TODO
* Check if there's enough space in the destination before doing the backup. Also automatically delete old backups.
* Manage the backups in a way similar to Time Machine - hourly backups for the past 24 hours; daily backups for the past month; weekly backups for the previous months.
* Minor changes (see TODO comments in the source).
# LICENSE
[MIT](http://opensource.org/licenses/MIT)
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/laurent22/rsync-time-backup/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
+96 -38
View File
@@ -27,6 +27,34 @@ fn_terminate_script() {
trap 'fn_terminate_script' SIGINT
# -----------------------------------------------------------------------------
# Small utility functions for reducing code duplication
# -----------------------------------------------------------------------------
fn_parse_date() {
# Converts YYYY-MM-DD-HHMMSS to YYYY-MM-DD HH:MM:SS and then to Unix Epoch.
case "$OSTYPE" in
linux*) date -d "${1:0:10} ${1:11:2}:${1:13:2}:${1:15:2}" +%s ;;
darwin*) date -j -f "%Y-%m-%d-%H%M%S" "$1" "+%s" ;;
esac
}
fn_find_backups() {
find "$DEST_FOLDER" -type d -name "????-??-??-??????" -prune
}
fn_expire_backup() {
# Double-check that we're on a backup destination to be completely
# sure we're deleting the right folder
if [ -z "$(fn_is_backup_destination "$(dirname -- "$1")")" ]; then
fn_log_error "$1 is not on a backup destination - aborting."
exit 1
fi
fn_log_info "Expiring $1"
rm -rf -- "$1"
}
# -----------------------------------------------------------------------------
# Source and destination information
# -----------------------------------------------------------------------------
@@ -53,15 +81,10 @@ fn_backup_marker_path() {
}
fn_is_backup_destination() {
DEST_MARKER_FILE="$(fn_backup_marker_path $1)"
if [ -f "$DEST_MARKER_FILE" ]; then
echo "1"
else
echo "0"
fi
find "$(fn_backup_marker_path "$1")" 2>/dev/null
}
if [ "$(fn_is_backup_destination $DEST_FOLDER)" != "1" ]; then
if [ -z "$(fn_is_backup_destination $DEST_FOLDER)" ]; then
fn_log_info "Safety check failed - the destination does not appear to be a backup folder or drive (marker file not found)."
fn_log_info "If it is indeed a backup folder, you may add the marker file by running the following command:"
fn_log_info ""
@@ -74,15 +97,25 @@ fi
# Setup additional variables
# -----------------------------------------------------------------------------
BACKUP_FOLDER_PATTERN="^[[:digit:]][[:digit:]][[:digit:]][[:digit:]]-[[:digit:]][[:digit:]]-[[:digit:]][[:digit:]]-[[:digit:]][[:digit:]][[:digit:]][[:digit:]][[:digit:]][[:digit:]]$"
export IFS=$'\n' # Better for handling spaces in filenames.
NOW=$(date +"%Y-%m-%d-%H%M%S")
PROFILE_FOLDER="$HOME/.rsync_tmbackup"
LOG_FILE="$PROFILE_FOLDER/$NOW.log"
DEST=$DEST_FOLDER/$NOW
LAST_TIME=$(ls -1 -- "$DEST_FOLDER" | grep "$BACKUP_FOLDER_PATTERN" | tail -n 1)
PREVIOUS_DEST=$DEST_FOLDER/$LAST_TIME
PREVIOUS_DEST=$(fn_find_backups | sort | tail -n 1)
INPROGRESS_FILE=$DEST_FOLDER/backup.inprogress
case "$OSTYPE" in
linux*)
KEEP_ALL_DATE=$(date -d '-1 day' +%s)
KEEP_DAILIES_DATE=$(date -d '-1 month' +%s)
;;
darwin*)
KEEP_ALL_DATE=$(date -j -f "%a %b %d %T %Z %Y" "`date -v -1d`" "+%s")
KEEP_DAILIES_DATE=$(date -j -f "%a %b %d %T %Z %Y" "`date -v -1m`" "+%s")
;;
esac
# -----------------------------------------------------------------------------
# Create profile folder if it doesn't exist
# -----------------------------------------------------------------------------
@@ -97,19 +130,18 @@ fi
# -----------------------------------------------------------------------------
if [ -f "$INPROGRESS_FILE" ]; then
if [ "$LAST_TIME" != "" ]; then
if [ "$PREVIOUS_DEST" != "" ]; then
# - Last backup is moved to current backup folder so that it can be resumed.
# - 2nd to last backup becomes last backup.
fn_log_info "$INPROGRESS_FILE already exists - the previous backup failed or was interrupted. Backup will resume from there."
LINE_COUNT=$(ls -1 -- "$DEST_FOLDER" | grep "$BACKUP_FOLDER_PATTERN" | tail -n 2 | wc -l)
LINE_COUNT=$(fn_find_backups | sort | tail -n 2 | wc -l)
mv -- "$PREVIOUS_DEST" "$DEST"
if [ "$LINE_COUNT" -gt 1 ]; then
SECOND_LAST_TIME=$(ls -1 -- "$DEST_FOLDER" | grep "$BACKUP_FOLDER_PATTERN" | tail -n 2 | head -n 1)
LAST_TIME=$SECOND_LAST_TIME
PREVIOUS_PREVIOUS_DEST=$(fn_find_backups | sort | tail -n 2 | head -n 1)
PREVIOUS_DEST=$PREVIOUS_PREVIOUS_DEST
else
LAST_TIME=""
PREVIOUS_DEST=""
fi
PREVIOUS_DEST=$DEST_FOLDER/$LAST_TIME
fi
fi
@@ -121,7 +153,7 @@ while [ "1" ]; do
# -----------------------------------------------------------------------------
LINK_DEST_OPTION=""
if [ "$LAST_TIME" == "" ]; then
if [ "$PREVIOUS_DEST" == "" ]; then
fn_log_info "No previous backup - creating new one."
else
# If the path is relative, it needs to be relative to the destination. To keep
@@ -140,6 +172,42 @@ while [ "1" ]; do
mkdir -p -- "$DEST"
fi
# -----------------------------------------------------------------------------
# Purge certain old backups before beginning new backup.
# -----------------------------------------------------------------------------
# Default value for $prev ensures that the most recent backup is never deleted.
prev="0000-00-00-000000"
for fname in $(fn_find_backups | sort -r); do
date=$(basename "$fname")
stamp=$(fn_parse_date $date)
# Skip if failed to parse date...
# TODO: display warning
[ -n "$stamp" ] || continue
if [ $stamp -ge $KEEP_ALL_DATE ]; then
true
elif [ $stamp -ge $KEEP_DAILIES_DATE ]; then
# Delete all but the most recent of each day.
if [ ${date:0:7} == ${prev:0:7} ]; then
[ ${date:8:2} -eq ${prev:8:2} ] && fn_expire_backup "$fname"
fi
else
# Delete all but the most recent of each month.
if [ ${date:0:4} == ${prev:0:4} ]; then
[ ${date:5:2} -eq ${prev:5:2} ] && fn_expire_backup "$fname"
fi
fi
prev=$date
done
# -----------------------------------------------------------------------------
# Start backup
# -----------------------------------------------------------------------------
@@ -189,9 +257,9 @@ while [ "1" ]; do
grep --quiet "Result too large (34)" "$LOG_FILE"
NO_SPACE_LEFT="$?"
fi
rm -- "$LOG_FILE"
if [ "$NO_SPACE_LEFT" == "0" ]; then
# TODO: -y flag
read -p "It looks like there is no space left on the destination. Delete old backup? (Y/n) " yn
@@ -200,31 +268,21 @@ while [ "1" ]; do
esac
fn_log_warn "No space left on device - removing oldest backup and resuming."
BACKUP_FOLDER_COUNT=$(ls -1 $DEST_FOLDER | grep "$BACKUP_FOLDER_PATTERN" | wc -l)
BACKUP_FOLDER_COUNT=$(fn_find_backups | wc -l)
if [ "$BACKUP_FOLDER_COUNT" -lt "2" ]; then
fn_log_error "No space left on device, and no old backup to delete."
exit 1
fi
OLDEST_BACKUP=$(ls -1 $DEST_FOLDER | grep "$BACKUP_FOLDER_PATTERN" | head -n 1)
if [ "$OLDEST_BACKUP" == "" ]; then
OLD_BACKUP_PATH=$(fn_find_backups | head -n 1)
if [ "$OLD_BACKUP_PATH" == "" ]; then
fn_log_error "No space left on device, and cannot get path to oldest backup to delete."
exit 1
fi
OLD_BACKUP_PATH="$DEST_FOLDER/$OLDEST_BACKUP"
# Double-check that we're on a backup destination to be completely sure we're deleting the right folder
OLD_BACKUP_PARENT_PATH=$(dirname -- "$OLD_BACKUP_PATH")
if [ "$(fn_is_backup_destination $OLD_BACKUP_PARENT_PATH)" != "1" ]; then
fn_log_error "'$OLD_BACKUP_PATH' is not on a backup destination - aborting."
exit 1
fi
fn_log_info "Deleting '$OLD_BACKUP_PATH'..."
rm -rf -- "$OLD_BACKUP_PATH"
fn_expire_backup "$OLD_BACKUP_PATH"
# Resume backup
continue
fi
@@ -233,7 +291,7 @@ while [ "1" ]; do
fn_log_error "Exited with error code $RSYNC_EXIT_CODE"
exit $RSYNC_EXIT_CODE
fi
rm -- "$INPROGRESS_FILE"
# TODO: grep for "^rsync error:.*$" in log
fn_log_info "Backup completed without errors."