From ace92010ec9067669c1cdaf079874d3949bed9da Mon Sep 17 00:00:00 2001 From: Matteo Cypriani Date: Sun, 1 Jun 2014 20:48:06 -0400 Subject: [PATCH] [file_utils] dirpacker: --move, --prefix, -v/-q dirpacker can now create a directory for each volume and move the corresponding files in it (option --move). The option --prefix allows the user to specify a custom prefix for the volumes' names. The options -v (--verbose) and -q (--quiet) enable or disable displaying the list of volumes. --- file_utils/README | 11 ++++-- file_utils/dirpacker.py | 77 ++++++++++++++++++++++++++++++----------- 2 files changed, 64 insertions(+), 24 deletions(-) diff --git a/file_utils/README b/file_utils/README index 58289ee..44d940d 100644 --- a/file_utils/README +++ b/file_utils/README @@ -11,10 +11,15 @@ The particularity of this program, compared for example to datapacker instead of regular files only. The files inside a directory won't be separated on several archives, they will be on the same volume. -Note: I kept the datapacker's terminology, a volume is also called a -bin. +Note: I kept the datapacker's terminology in which a volume is also +called a bin. -To see the usage, call the program with -h. +By default, dirpacker displays a list of bins and the files they +would contain, along with the size of each file and some statistics. +With the option --move, a directory will be created for each volume and +the files will be moved to the corresponding volume. + +To see the full usage, call the program with -h. # mvparent.sh # diff --git a/file_utils/dirpacker.py b/file_utils/dirpacker.py index b2ebf75..2308916 100755 --- a/file_utils/dirpacker.py +++ b/file_utils/dirpacker.py @@ -32,6 +32,7 @@ import argparse import os +import shutil from collections import defaultdict import math @@ -51,13 +52,23 @@ class Bin: def print(self): """Displays the contents of the bin. """ - print("\n### Bin #{} ###\n# List of files:".format(self.id)) + print("\n### {} ###\n# List of files:".format(self.name())) for filename, size in sorted(self.files.items()): print("{} # {:.2f} MiB".format(filename, size)) print("# This bin's size: {:.2f} MiB".format(self.size)) sizefree = options.maxbinsize - self.size print("# Free space: {:.2f} MiB".format(sizefree)) + def name(self): + """Returns the bin's name' + """ + return "{}{:02}".format(options.prefix, b.id) + + def list_files(self): + """Returns a list of the files contained in the bin. + """ + return list(self.files.keys()) + def du(basepath): """ Returns the size of the file of directory `basepath`, in MiB. @@ -107,9 +118,14 @@ arg_parser.add_argument("-s", "--size", action="store", dest="maxbinsize", help="maximal size of each volume (bin), in MiB; the " "default is 703 MiB, i.e. the size of a 80-minute " "CD-ROM") -arg_parser.add_argument("-a", "--action", action="store", default="list", - help='action to be taken; can be "list" (default)' - ' or "move" (move input to per-volume directories)') +arg_parser.add_argument("--prefix", action="store", default="bin_", + help="prefix of a bin's name (default: \"bin_\")") +arg_parser.add_argument("--move", action="store_true", + help="move input to per-volume directories") +arg_parser.add_argument("-v", "--verbose", action="store_true", default=True, + help="be verbose (this is the default)") +arg_parser.add_argument("-q", "--quiet", action="store_false", dest="verbose", + help="print only warnings and errors") arg_parser.add_argument("filenames", metavar="file", nargs="+", help="files or directories to pack") options = arg_parser.parse_args() @@ -117,7 +133,8 @@ options = arg_parser.parse_args() ### Preliminary statistics ### -print("# Maximum size of a bin: {:.2f} MiB".format(options.maxbinsize)) +if options.verbose: + print("# Maximum size of a bin: {:.2f} MiB".format(options.maxbinsize)) # Compute the size of all the files sizes = defaultdict(list) @@ -134,12 +151,13 @@ for filename in options.filenames: sizes[size].append(filename) totalsize += size -print("# Total size of the input files: {:.2f} MiB".format(totalsize)) -minbins = math.ceil(totalsize / options.maxbinsize) -print("# Minimal (optimal) number of bins required: ", minbins) -sizefree = minbins * options.maxbinsize - totalsize -print("# Theoretical unused space with {} bins: {:.2f} MiB" - .format(minbins, sizefree)) +if options.verbose: + print("# Total size of the input files: {:.2f} MiB".format(totalsize)) + minbins = math.ceil(totalsize / options.maxbinsize) + print("# Minimal (optimal) number of bins required: ", minbins) + sizefree = minbins * options.maxbinsize - totalsize + print("# Theoretical unused space with {} bins: {:.2f} MiB" + .format(minbins, sizefree)) ### Assemble the bins ### @@ -154,24 +172,41 @@ binscreated = binnumber - 1 ### Final statistics ### -sizefree = binscreated * options.maxbinsize - totalsize -print("""\ +if options.verbose: + sizefree = binscreated * options.maxbinsize - totalsize + print("""\ # {} bins created. # Actual unused space over the {} bins created: {:.2f} MiB""" - .format(binscreated, binscreated, sizefree)) + .format(binscreated, binscreated, sizefree)) ### Execute the requested action ### -if options.action == "list": + +# Print the contents of the bins +if options.verbose: for b in bins: b.print() -elif options.action == "move": - print('Action "move" is not implemented yet.') - exit(0) -else: - print('ERROR! Action "{}" is unknown.'.format(options.action)) - exit(1) + +# Move the files +if options.move: + for b in bins: + dirname = b.name() + if os.path.exists(dirname) and not os.path.isdir(dirname): + print('# WARNING! File "{}" exists but is not a directory:' + ' skipping bin #{}.' + .format(dirname, b.id)) + continue + # Create the target directory: + try: + os.makedirs(dirname, exist_ok=True) + except FileExistsError: + print('# WARNING! Directory "{}" already exists with different' + ' permissions (mode). Proceeding anyway.' + .format(dirname)) + # Move the files in the directory: + for filename in b.list_files(): + shutil.move(filename, dirname) ### Wrapping-up ###