""" Flash backup tool by Connelly Barnes, public domain. Syntax: python backup.py rootsrcdir srcdir1 [srcdir2 ... srcdirn] backupdir Backs up srcdir1 ... srcdirn to backupdir. The rootsrcdir prefix must be a prefix of all source directories, and rootsrcdir maps to backupdir. Prints a summary of copying operations which will take place during the backup and asks for confirmation before any disc writes are made. Successive uses of backup.py will cause an existing backup in backupdir to be updated. Example: python backup.py D:\ D:\Art D:\Documents D:\Code D:\Pictures G:\Backup This copies D:\Art => G:\Backup\Art, D:\Documents => G:\Backup\Documents, etc. """ from autoimp import * def is_hidden_file(filename): return win32api.GetFileAttributes(filename) & win32con.FILE_ATTRIBUTE_HIDDEN def stat_stamp(st): return (st.st_size, st.st_mtime) def dir_parents(dirname): dirname = os.path.abspath(dirname) if dirname.endswith('\\') or dirname.endswith('/'): dirname = dirname[:-1] while True: if '\\' in dirname or '/' in dirname: yield dirname else: yield dirname + '\\' if '\\' in dirname: dirname = dirname[:dirname.rindex('\\')] elif '/' in dirname: dirname = dirname[:dirname.rindex('/')] else: return def prepare_backup(rootsrcdir, srcdirs, destdir, query=True, more_verbose=False): """ Prepare for backup, return (srcfiles, destfiles): two lists of the same length for file copying. If query is True then be verbose and ask for confirmation and call sys.exit() if not confirmed. """ if query: print 'Back up directories:' srcfiles = [] destfiles = [] total = 0 total_delta = 0 count = 0 for srcdir in srcdirs: dirsize = 0 dirdelta = 0 for (dirpath, dirnames, filenames) in os.walk(srcdir): assert dirpath[:len(srcdir)] == srcdir assert srcdir.startswith(rootsrcdir) relpath = dirpath[len(rootsrcdir):] if relpath.startswith('\\') or relpath.startswith('/'): relpath = relpath[1:] dest = os.path.join(destdir, relpath) if any(is_hidden_file(x) for x in dir_parents(dirpath)): continue for filename in filenames: fullname = os.path.join(dirpath, filename) if not is_hidden_file(fullname): src_stat = os.stat(fullname) destname = os.path.join(dest, filename) delta = src_stat.st_size if os.path.exists(destname): dest_stat = os.stat(destname) delta -= dest_stat.st_size if stat_stamp(src_stat) == stat_stamp(dest_stat): continue total += src_stat.st_size dirsize += src_stat.st_size dirdelta += delta total_delta += delta count += 1 srcfiles.append(fullname) destfiles.append(destname) if more_verbose: print 'Will copy', fullname, '=>', destname, ('(copy %.1f MB)' % (src_stat.st_size/1024**2.0)) if query: print srcdir, '=>', os.path.join(destdir, srcdir[len(rootsrcdir):]), '(copy %.3f GB, change %.3f GB)' % (dirsize/1024**3.0, dirdelta/1024**3.0) if query: print print 'Total copy size: %.3f GB' % (total / 1024**3.0) print 'Total change in size: %.3f GB' % (total_delta / 1024**3.0) print 'Number of files: ', count print s = raw_input('Continue (yes/no)?') if not s.lower().startswith('y'): sys.exit(1) return (srcfiles, destfiles) def do_backup(srcfiles, destfiles, verbose=True): """ Perform backup, copying srcfiles[i] => destfiles[i] for all i. """ print 'Copying:' assert len(srcfiles) == len(destfiles) for i in range(len(srcfiles)): destdir = os.path.split(destfiles[i])[0] if not os.path.exists(destdir): os.makedirs(destdir) if verbose: print srcfiles[i], '=>', destfiles[i] shutil.copyfile(srcfiles[i], destfiles[i]) shutil.copystat(srcfiles[i], destfiles[i]) print 'Done' def main(): args = sys.argv[1:] if len(args) < 3: print __doc__.strip() return rootsrcdir = args[0] srcdirs = args[1:-1] destdir = args[-1] (srcfiles, destfiles) = prepare_backup(rootsrcdir, srcdirs, destdir) do_backup(srcfiles, destfiles) if __name__ == '__main__': main()