#!/usr/bin/python3 """Rename 'bad' characters to underscores in a filesystem hierarchy.""" import os import sys # We map these to underscores, for the sake of my Android phone which is probably using a FAT filesystem BAD_CHARACTERS = set(':()[]&!?"\'\\*') def contains_bad_char(filename): """Return True iff filename contains one of the 'bad characters'.""" return bool(set(filename) & BAD_CHARACTERS) def obtain_dirnames(root): """Get the troublesome directories in root.""" # Note that this sometimes needs to be run more than once, but it gets where we're going after a run or two troublesome_paths = [] for root, directories, filenames in os.walk(root, topdown=False): for dirname in directories: path = os.path.join(root, dirname) if contains_bad_char(path): sys.stderr.write('Got bad path {}\n'.format(path)) troublesome_paths.append(path) troublesome_paths.sort() return troublesome_paths def obtain_filenames(root): """Get the troublesome regular (non-directory) filenames in root.""" troublesome_paths = [] for root, directories, filenames in os.walk(root): for filename in filenames: path = os.path.join(root, filename) if contains_bad_char(path): sys.stderr.write('Got bad path {}\n'.format(path)) troublesome_paths.append(path) troublesome_paths.sort() return troublesome_paths def replace(old_filename): """Replace bad characters with underscore.""" new_filename = old_filename for bad_char in BAD_CHARACTERS: new_filename = new_filename.replace(bad_char, '_') return new_filename def main(): """Kick off replacement in each directory hierarchy specified in argv[1:].""" for directory in sys.argv[1:]: # We do directories first. dirnames = obtain_dirnames(directory) for old_dirname in dirnames: new_dirname = replace(old_dirname) sys.stderr.write('Renaming directory {} to {}\n'.format(old_dirname, new_dirname)) os.rename(old_dirname, new_dirname) # Then we re-traverse to get filenames filenames = obtain_filenames(directory) for old_filename in filenames: new_filename = replace(old_filename) sys.stderr.write('Renaming {} to {}\n'.format(old_filename, new_filename)) os.rename(old_filename, new_filename) main()