Skip to content

Instantly share code, notes, and snippets.

@morhekil
Last active June 26, 2020 16:18
  • Star 10 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save morhekil/8382294 to your computer and use it in GitHub Desktop.
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
@neidiom
Copy link

neidiom commented Jun 15, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment