Created
March 20, 2020 16:45
-
-
Save michaeldye/05af2c7bee523e691c24c3a19d07707a to your computer and use it in GitHub Desktop.
A simple differential backup script that uses hard links to manage trees of filesystem backup deltas (heavy lifting done by rsync)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash -x | |
# Backup scheme description | |
# | |
# This script is intended to prevent data loss from hardware failure or | |
# accidental deletion by performing regular (perhaps nightly) differential | |
# backups against a max period full backup (perhaps weekly). | |
# | |
# 0 is newest backup, largest number is the oldest. Script keeps 0 to | |
# (N-1) differential backups on system, Nth is a full. This script was tested | |
# only with N_BACKUPS >= 3 | |
N_BACKUPS=15 | |
# | |
# TODO: 1. option to send content of a most recent backup after success to a remote system as a tar, optionally encrypted (streaming way? maybe a dynamic sshfs mount?) | |
# | |
if [ "$#" -lt 3 ]; then | |
echo "usage: $0 hostname src_path dst_dir [exclude_file_path]" | |
exit 1 | |
fi | |
function rotate_all() { | |
local path_prefix=$1; shift | |
local ct_max=$((N_BACKUPS-1)) | |
#TODO: dangerous! find a way to make this safer | |
sudo rm -rf "${path_prefix}$ct_max" | |
for ix in $(seq 1 $ct_max); do | |
tix=$((ct_max - ix)) | |
sudo mv "${path_prefix}${tix}" "${path_prefix}$((tix+1))" | |
done | |
} | |
if [[ "$1" == 'localhost' ]] || [[ "$1" == "$(hostname)" ]]; then | |
SOURCE="$2" | |
else | |
SOURCE="$1:$2" | |
fi | |
# --itemize-changes | |
ARGS="--archive --delete --one-file-system --hard-links --inplace --numeric-ids" | |
if [ -e "$4" ]; then | |
ARGS="$ARGS --exclude-from "$4"" | |
fi | |
# replace all /'s with _'s in path | |
DIR=${2//\//_} | |
# rem leading _ | |
DIR=${DIR/_/} | |
PATH_PREFIX="$3/$1/$DIR-backup." | |
if [ -d "${PATH_PREFIX}0" ]; then | |
if [ "$(ls ${PATH_PREFIX}0/backup_completed_on*)" == "" ]; then | |
# dst dir exists but contains unsuccessful backup, remove it | |
echo "Backup in ${PATH_PREFIX}0 failed previously, deleting it and keeping older backups" | |
sudo rm -rf "${PATH_PREFIX}0" | |
else | |
# dst dir exists and it contains a successful backup, try to rotate | |
rotate_all "${PATH_PREFIX}" | |
ARGS="$ARGS --link-dest=${PATH_PREFIX}1/" | |
echo "Rotated backup dirs" | |
fi | |
else | |
mkdir -p $(dirname "$PATH_PREFIX") | |
fi | |
CMD="rsync $ARGS "$SOURCE" "${PATH_PREFIX}0"" | |
echo "$CMD" | |
eval "$CMD" | |
touch "${PATH_PREFIX}0/backup_completed_on-$(date +%F_%T)" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment