rsync_backup /

# -*- coding: ascii
rsync-based backup with space-efficient hardlinks,
multiple generations, extra token for daily, weekly,
monthly, ... backups.

author: Thomas Waldmann
license: BSD

import os
from os.path import exists, abspath
import sys
import errno
from subprocess import call as cmd
import shutil

RSYNC = "rsync -avH --numeric-ids --link-dest=%(link_dst)s %(src)s/ %(dst)s/"

def join(path, n, token):
    if n == 0:
        # there is only ONE "0" directory, no token appended
        token = ""
    return os.path.join(path, "%d%s" % (n, token))

def rsync(src, dst, link_dst):
    """backup contents of src directory into dst directory"""
    return cmd(RSYNC % dict(src=abspath(src),

def dirshift(path, n, token):
    """shift the directories below the base path:
       N-1 -> N (kill it later)
       N-2 -> N-1
       0 -> 1
       create an empty 0
    for i in range(n, 0, -1):
            os.rename(join(path, i-1, token),
                      join(path, i, token))
        except OSError as err:
            if err.errno != errno.ENOENT:
                # ignore if directory does not exist,
                # otherwise raise
    os.mkdir(join(path, 0, token))
    del_dir = join(path, n, token)
    if exists(del_dir):

def backup(src, dst, n, token=""):
    """do a backup from src to dst, shifting dirs first"""
    dirshift(dst, n, token)
    return rsync(src, join(dst, 0, token), join(dst, 1, token))

if __name__ == "__main__":
    if len(sys.argv) != 5:
        print "rsb N token src dst"
    n, token, src, dst = sys.argv[1:]
    rc = backup(src, dst, int(n), token)