#!/usr/bin/env python """ A file/directory copy utility. The aim for this is to display status/ETA informations when copying files under the console. """ import sys, time, os, getopt, logging __version__ = '0.2' __author__ = 'Philippe Normand ' CHUNK_SIZE = 8192 log = sys.stderr.write class FileNotFound(Exception): pass class Progress: '''Progress display for console applications. Written by Richard Jones. http://www.mechanicalcat.net/richard/log/Python/Progress_display ''' def __init__(self, info, sequence): self.info = info self.sequence = iter(sequence) self.total = len(sequence) self.start = self.now = time.time() self.num = 0 self.stepsize = self.total / 100 or 1 self.steptimes = [] self.display() def __iter__(self): return self def next(self): self.num += 1 if self.num > self.total: print self.info, 'done', ' '*(75-len(self.info)-6) sys.stdout.flush() return self.sequence.next() if self.num % self.stepsize: return self.sequence.next() self.display() return self.sequence.next() def display(self): # figure how long we've spent - guess how long to go now = time.time() steptime = now - self.now self.steptimes.insert(0, steptime) if len(self.steptimes) > 5: self.steptimes.pop() steptime = sum(self.steptimes) / len(self.steptimes) self.now = now eta = steptime * ((self.total - self.num)/self.stepsize) # tell it like it is (or might be) if now - self.start > 3: M = eta / 60 H = M / 60 M = M % 60 S = eta % 60 s = '%s %2d%% (ETA %02d:%02d:%02d)'%(self.info, self.num * 100. / self.total, H, M, S) else: s = '%s %2d%%'%(self.info, self.num * 100. / self.total) sys.stdout.write(s + ' '*(75-len(s)) + '\r') sys.stdout.flush() def copyFile(aFile, newFile,moveMode): " copy aFile to newFile " if not os.path.exists(aFile): raise FileNotFound(aFile) st = os.stat(aFile) fileSize = st.st_size f1 = open(aFile) f2 = open(newFile,'w') fileName = os.path.split(aFile)[-1] chunks = makeChunkList(fileSize) try: for chunk in Progress(fileName, chunks): f2.write(f1.read(chunk)) finally: f1.close() f2.close() os.chmod(newFile,st.st_mode) if moveMode: os.remove(aFile) def copyDirectory(fromDir,toDir,moveMode): " copy directory fromDir to a new directory toDir " if not os.path.isdir(toDir): os.mkdir(toDir) for file in os.listdir(fromDir): fromLocation = os.path.join(fromDir,file) destination = os.path.join(toDir,file) if os.path.isdir(fromLocation): copyDirectory(fromLocation,destination,moveMode) else: copyFile(fromLocation,destination,moveMode) def makeChunkList(fileSize): " split a high number into chunks " chunksNb, end = divmod(fileSize, CHUNK_SIZE) chunks = [ CHUNK_SIZE for i in range(chunksNb) ] if end > 0: chunks.append(end) return chunks def usage(prog): " progessCopy usage informations " print """\ This is a tool to show status informations while copying files and/or directories. Use it like GNU CP(1). Usage: %s [OPTIONS] ... where OPTIONS can be: -h | --help -> this message --version -> licence & version informations -m -> move files instead of copying them -r -> recurse directory copy """ % prog sys.exit(0) def version(prog): " progressCopy versioning/licence informations " print """\ progressCopy version %s is a Free Software released under the GPLv2 or higher. Contact %s for feedback (bugs,...) """ % (__version__, __author__) sys.exit(0) def main(): try: optlist,args = getopt.getopt(sys.argv[1:],"rmh",["version","help"]) except getopt.GetoptError: usage(sys.argv[0]) moveMode, recurseCopy = False, False for o,a in optlist: if o in ('-h','--help'): usage(sys.argv[0]) elif o == '--version': version(sys.argv[0]) elif o == '-r': recurseCopy = True elif o == '-m': moveMode = True else: log("Unknown option : '%s'\n" % o) i, files = 0, [] for arg in sys.argv[1:]: if arg[0] != '-': files.append(os.path.expanduser(arg)) try: while i < len(files)-1: if files[i].endswith('/'): files[i] = files[i][:-1] sourceFilename = os.path.split(files[i])[1] newFilePath = os.path.join(files[-1],sourceFilename) if os.path.isdir(files[i]): if recurseCopy: if os.path.isdir(files[-1]): copyDirectory(files[i], newFilePath, moveMode) else: log("'%s' should be a directory\n" % files[-1]) else: log("Not in recursive mode... directory: '%s' won't be processed\n"% files[i]) elif os.path.isdir(files[-1]): copyFile(files[i], newFilePath, moveMode) else: copyFile(files[i], files[-1], moveMode) i += 1 except FileNotFound: log("File not found: '%s'\n" % files[i]) return 1 else: return 0 if __name__ == "__main__": sys.exit(main())