Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Shell script to handle incremental data backups with rsync. Optimised to handle large number of infrequently changing files, by using hard links to save on used disk space. For more details - http://localhost:4000/2014/01/12/automatic-backups-with-ruby-and-linux-shell-part-3/
#!/bin/bash
# Rsync based file backup script, with hard-linking enabled.
#
# Runtime options:
# -n - dry-run; do nothing, just display what is going to happen on a real run
# -v - verbose output; print out what is being backed up
#
# set to your rsync location
RSYNC_BIN="/usr/bin/rsync"
# location of the files to back up (WITH a trailing slash)
SOURCE_DIR="/path/to/source_dir/"
# target dir for backups (NO trailing slash)
BACKUP_DIR="/path/to/backup_dir"
# how many backups to keep
KEEP_BACKUPS=7
#
# END OF CONFIGURATION
#
# takes command to execute as the first argument, and then
# 1. Prints it if we're in verbose mode
# 2. If we're in dry-run mode, as the second argument is nil - does nothing
# 3. If we're not in dry-run mode - executes the command
# 4. If we're in dry-run mode, but the 2nd argument is not nill - executes the commad
# (i.e. when it's constructed to dry-run by itself)
function docmd {
if [ "$verbose" -eq 1 ]; then echo "--- $1"; fi
# if [ [ "$dry_run" -eq 0 ] -o [ -n $2 ] ]; then `$1`; fi
if [ "$dry_run" -eq 0 ] || [ -n "$2" ]; then $1; fi
}
# reset getopts
OPTIND=1
# set default values for configuration params
dry_run=0
verbose=0
while getopts "vn" opt; do
case "$opt" in
v) verbose=1;
;;
n) dry_run=1; verbose=1;
;;
esac
done
# generate directory name that we'll be syncing into
timestamp=`date +"%Y.%m.%d.%H.%M.%S"`
sync_target="$BACKUP_DIR/$timestamp"
# extra rsync options
rsync_options=""
# get a directory to link against, if any
link_target=`ls -1 $BACKUP_DIR | tail -n1`
# if we have a valid link directory - set it as an option for rsync
if [ -n "$link_target" ]; then rsync_options="--link-dest=$BACKUP_DIR/$link_target"; fi
# if we're in verbose mode - set it for rsync
if [ "$verbose" -eq 1 ]; then rsync_options="$rsync_options -v"; fi
# if we're in dry run mode - set it for rsync
if [ "$dry_run" -eq 1 ]; then rsync_options="$rsync_options -n"; fi
docmd "mkdir -p $sync_target"
docmd "$RSYNC_BIN -a --delete $rsync_options $SOURCE_DIR $sync_target" 1
backups_count=`ls -1 $BACKUP_DIR | wc -l`
backups_to_remove=$(($backups_count-$KEEP_BACKUPS))
if [ $backups_to_remove -gt 0 ]; then
for d in `ls -1 $BACKUP_DIR | head -n$backups_to_remove`; do
docmd "rm -rf $BACKUP_DIR/$d"
done
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment