#!/usr/bin/env python # -*- coding: UTF-8 -*- # Licensed under the terms of the GNU GPLv3 or later # Proprietary Software Must Perish! import os import re import sys from optparse import OptionParser USAGE = "%prog [OPTION]... PATHNAME..." VERSION = "%prog 0.2" DESCRIPTION = """%prog runs through a list of given PATHNAMEs, trying to rename them in a way that makes sense for heavy users of the bourne shell (e. g. by stripping whitespace and replacing problematic characters).""" op = OptionParser(version = VERSION, usage = USAGE, description = DESCRIPTION ) op.add_option("-v", "--verbose", action = "store_true", dest = "verbose", help = "be verbose about what is going on") op.add_option("-n", "--no-dirs", action = "store_true", dest = "nodirs", help = "do not rename directories") op.add_option("-d", "--dry-run", action = "store_true", dest = "dryrun", help = "do not modify the filesystem/actually rename inodes") op.add_option("-r", "--recursive", action = "store_true", dest = "recur", help = "recursively process directories") op.add_option("-s", "--subst", action = "store", dest = "subst_str", default = "_", help = "substitution string for problematic characters (Default: _)") op.add_option("-p", "--preserve-case", action = "store_true", dest = "preserve_case", default = False, help = "do not convert names to lowercase") op.add_option("-c", "--collapse-underscores", action = "store_true", dest = "collapse_underscores", default = False, help = "collapse multiple occurences of _ to a single one") (opts, args) = op.parse_args() #hardcoded substitution table; annoying chars will be replaced by #opts.subst_str subst_list = [ (r'ä', 'ae'), (r'ö', 'oe'), (r'ü', 'ue'), (r'ß', 'sz'), (r'[]})({[ \t\n!"§$%&?`\'\\:*~°<>|^@]', opts.subst_str), ] if opts.collapse_underscores == True: subst_list.append((r'__*', '_')) #yuck! some day, this'll have to deal with unicode properly, I guess... if opts.preserve_case == True: subst_additional_pairs = [(r'Ä', 'Ae'), (r'Ö', 'Oe'), (r'Ü', 'Ue')] else: subst_additional_pairs = [(r'Ä', 'ae'), (r'Ö', 'oe'), (r'Ü', 'ue')] for subst_pair in subst_additional_pairs: subst_list.append(subst_pair) replace_list = [] for replace_seq in subst_list: replace_list.append((re.compile(replace_seq[0], (re.U)), replace_seq[1])) #file-counter for verbose modes f_seen = 0 f_renamed = 0 def sanitize_name(current_path): global f_seen global f_renamed f_seen += 1 if (opts.nodirs and os.path.isdir(current_path)): if opts.verbose: print >> sys.stderr, current_path, "is a directory, skipping." return True (current_dir, current_file) = os.path.split(current_path) if opts.preserve_case == False: clean_file = current_file.lower() else: clean_file = current_file for (old, new) in replace_list: clean_file = re.sub(old, new, clean_file, 0) if clean_file != current_file: #avoid overwriting existing files, in a way similar to wget(1) while os.path.exists(os.path.join(current_dir, clean_file)): clean_file = clean_file + ".1" if opts.verbose: print >> sys.stderr, current_path, "->", os.path.join(current_dir, clean_file) if not opts.dryrun: try: os.rename(os.path.join(current_dir, current_file), os.path.join(current_dir, clean_file)) except Exception, e: print >> sys.stderr, "An error occured while trying to rename", current_path, e return False f_renamed += 1 return True #main processing loop if opts.recur == True: for pathname in args: if(os.path.isdir(pathname)): for root, dirs, files in os.walk(pathname, topdown = False): for name in files: sanitize_name(os.path.join(root, name)) for name in dirs: sanitize_name(os.path.join(root, name)) else: sanitize_name(os.path.join(root, pathname)) else: for pathname in args: sanitize_name(pathname) if opts.verbose: print >> sys.stderr, f_renamed, "of", f_seen, "files renamed." if f_seen == 0: print >> sys.stderr, "No pathnames given.\n" op.print_help() if opts.dryrun: print >> sys.stderr, "Dry-run requested; no changes were made."