Last active
August 29, 2015 14:20
-
-
Save PetrGlad/448abdeb8710f6611fd8 to your computer and use it in GitHub Desktop.
bak.py
This file contains hidden or 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
DOC = """ | |
This script creates/restores_from backup copy of given | |
file (pushes/pops backup file into/from stack of backups) | |
Revision 3 | |
Caveat: This script may work incorrectly if there are pre-existing | |
files which look like backups i.e. files matching ".+\.#[a-zA-Z]*[0-9]+" | |
Examples: | |
make backup copy of a.txt | |
python bak.py a.txt | |
make backup copy of sub directory a | |
python bak.py a | |
restore file from latest backup file | |
python bak.py --restore a.txt | |
find out what files look like backups in current dir | |
python bak.py --list . | |
Copygight (C) 2004, Petr Gladkikh | |
You can redistribute and/or modify this program under the terms | |
of the Common Public Licence version 1.0 as published by | |
Open Source Initiative corporation. Full text of the | |
licence can be found at http://opensource.org/licenses/cpl.php | |
""" | |
import sys | |
import shutil | |
import os | |
from os import path | |
import re | |
import tarfile | |
import glob | |
SEPARATOR = ".#" | |
def make_copy(a, b): | |
# print "make_copy", a, b # debug | |
if os.path.isdir(a): | |
shutil.copytree(a, b) | |
else: | |
shutil.copyfile(a, b) | |
def tar_store(a, b): | |
# print "tar_store", a, b # debug | |
tar = tarfile.open(b, "w:bz2") | |
tar.add(a) | |
tar.close() | |
def tar_restore(a, b): | |
# print "tar_restore", a, b # debug | |
tar = tarfile.open(a, "r") | |
for tarinfo in tar: | |
tar.extract(tarinfo) | |
tar.close() | |
def match_last(regex, str): | |
"return last match of regex in given string" | |
mi = regex.finditer(str) | |
match = None | |
try: | |
while 1: | |
match = mi.next() | |
except StopIteration: | |
pass | |
return match | |
def parse_bak_name(filename): | |
""" Parse bak into (filename, index, method) | |
or None if it does not seem like a bak """ | |
rex = re.compile( | |
"(?P<sfx>" + re.escape(SEPARATOR) + ")" | |
+ "(?P<mtd>[a-zA-Z]*)" | |
+ "(?P<idx>[0-9]+)$") | |
match = match_last(rex, filename) | |
if match != None: | |
fn = filename[0 : match.start('sfx')] | |
id = eval(match.group('idx')) | |
m = match.group('mtd') | |
return (fn, id, m) # original_filename, depth, method_id | |
else: | |
return None | |
def make_bak_name(filename, method, id): | |
"make bak filename with given depth" | |
return filename + SEPARATOR + method + `id` | |
def remove(a): | |
if os.path.exists(a): | |
if os.path.isdir(a): | |
shutil.rmtree(a) | |
else: | |
os.remove(a) | |
class Backup: | |
def push(self, filename, id): | |
print "Pushing", filename, "[" + `id` + "]" | |
bak_name = make_bak_name(filename, self.tag, id) | |
self.store(filename, bak_name) | |
def pop(self, filename, id): | |
print "Popping", filename, "[" + `id` + "]" | |
bak_name = make_bak_name(filename, self.tag, id) | |
remove(filename) | |
self.restore(bak_name, filename) | |
remove(bak_name) | |
class CopyBackup(Backup): | |
tag = "" | |
def store(self, from_name, to_name): | |
make_copy(from_name, to_name) | |
def restore(self, from_name, to_name): | |
make_copy(from_name, to_name) | |
class BzipBackup(Backup): | |
tag = "bz" | |
def store(self, from_name, to_name): | |
tar_store(from_name, to_name) | |
def restore(self, from_name, to_name): | |
tar_restore(from_name, to_name) | |
METHODS = [ CopyBackup(), BzipBackup() ] | |
def find_method(method_id): | |
method = None | |
for m in METHODS: | |
if m.tag == method_id: | |
return m | |
else: | |
return None | |
def list_baks(filename): | |
directory = os.path.dirname(filename) | |
fileonly = os.path.split(filename)[-1] | |
lst = {} | |
for fn in os.listdir(directory): | |
bak = parse_bak_name(fn) | |
if bak != None: | |
(fn, id, m) = bak | |
if fileonly == fn: | |
lst[id] = m | |
return lst | |
def get_top_bak(filename): | |
"find max suffix for given filename" | |
lst = list_baks(filename) | |
if len(lst) > 0: | |
id = max(lst) | |
return (id, lst[id]) | |
else: | |
return None | |
def apply_file_fn(filename, function): | |
if parse_bak(filename) == None: | |
function(filename) | |
def apply_fn(filename, function): | |
if os.path.isdir(filename): | |
for fn in os.listdir(filename): | |
apply_file_fn(os.path.join(filename, fn), function) | |
else: | |
apply_file_fn(filename, function) | |
def push(filename, push_method): | |
"make backup for given file" | |
tb = get_top_bak(filename) | |
if tb == None: | |
id = 0 | |
else: | |
id = tb[0] + 1 | |
push_method.push(filename, id) | |
def pop(filename): | |
"restore file from backup" | |
bk = get_top_bak(filename) | |
if bk == None: | |
print "No backups for", filename | |
else: | |
(id, method_id) = bk | |
method = find_method(method_id) | |
if method == None: | |
print "No handler for method '%s'" % method_id | |
else: | |
method.pop(filename, id) | |
def inventory(filename): | |
"list backups (print versions) for given file or directroy" | |
ids = list_baks(filename) | |
if len(ids) > 0: | |
lst = [] | |
for i in ids.iteritems(): | |
s = `i[0]` | |
if len(i[1]) > 0: | |
s += "("+i[1]+")" | |
lst.append(s) | |
import string | |
print filename, ":", string.join(lst, ", ") | |
else: | |
print "No backups for", filename | |
def cleanup(filename): | |
"remove all backups for given file" | |
for i in list_baks(filename).iteritems(): | |
fn = make_bak_name(filename, i[1], i[0]) | |
print "Deleting", fn | |
os.remove(fn) | |
if __name__ == '__main__': | |
from optparse import OptionParser | |
usage = "usage: %prog [options] filename1 [filename2 ...]" | |
parser = OptionParser(usage=usage) | |
parser.add_option("-z", "--pack", | |
action="store_true", dest="use_tar", default=False, | |
help="Use tar+bzip2 to store backup versions") | |
parser.add_option("-r", "--restore", | |
action="store_true", dest="do_restore", default=False, | |
help="Restore file (or directory) from last stored version") | |
parser.add_option("-l", "--list", | |
action="store_true", dest="list_files", default=False, | |
help="List backups for given file") | |
parser.add_option("-c", "--clean", | |
action="store_true", dest="do_cleanup", default=False, | |
help="Delete all backups for given file (see also --list option)") | |
parser.add_option("--about", | |
action="store_true", dest="show_about", default=False, | |
help="Print additional information about this program (see also --help option)") | |
(options, args) = parser.parse_args() | |
if options.show_about: | |
if options.list_files or options.do_cleanup: | |
parser.show_help() | |
else: | |
print DOC | |
elif options.list_files: | |
if options.do_cleanup: | |
parser.show_help() | |
else: | |
inventory(args[0]) | |
elif options.do_cleanup: | |
cleanup(args[0]) | |
else: | |
# file operations | |
for fname in args: | |
if options.do_restore: | |
if options.use_tar: | |
print "Error: -r or --restore option autodetects decompressing options.\n\t-z or --pack should not be specified." | |
else: | |
pop(fname) # method is autodetected | |
else: | |
if options.use_tar: | |
method = BzipBackup() | |
else: | |
method = CopyBackup() | |
push(fname, method) |
This file contains hidden or 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
python C:\Programs\bin\bak.py %* |
This file contains hidden or 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
python C:\Programs\bin\bak.py -z %* |
This file contains hidden or 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
python C:\Programs\bin\bak.py -r %1 %2 %3 %4 %5 %6 %7 %8 %9 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment