Skip to content

Instantly share code, notes, and snippets.

@glowinthedark
Last active December 6, 2022 18:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save glowinthedark/b0186679a2086675c6a099e4cdd6a0d1 to your computer and use it in GitHub Desktop.
Save glowinthedark/b0186679a2086675c6a099e4cdd6a0d1 to your computer and use it in GitHub Desktop.
replace files using regular expressions
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# NOTES
# - to search recursively in subfolders pass `--glob "**/*.*"`
import argparse
import re
import sys
from pathlib import Path
RED = "\033[31m"
GREEN = "\033[32m"
NOCOLOR = "\033[0m"
def rename_files(args):
if not args.search and (args.lowercase or args.uppercase):
args.search = '(^.*$)'
args.replace = '\\1'
root: Path = Path(args.root_folder)
for filepath in root.glob(args.glob):
colorized_old_filename = None
colorized_new_filename = None
new_filename = None
filename = filepath.name
if args.literal and args.search in filename:
# literal replace
new_filename = filename.replace(args.search, args.replace)
colorized_old_filename = filename.replace(args.search, f'{RED}{args.search}{NOCOLOR}')
colorized_new_filename = filename.replace(args.search, f'{GREEN}{args.replace}{NOCOLOR}')
elif not args.literal and re.compile(args.search).findall(filename):
new_filename = re.compile(args.search).sub(args.replace, filename)
colorized_old_filename = re.sub(bracketize_regex(args.search), f'{RED}\\1{NOCOLOR}', filename)
colorized_new_filename = re.compile(args.search).sub(f'{GREEN}{args.replace}{NOCOLOR}', filename)
# to lowercase only
if args.lowercase and new_filename:
new_filename = filename.lower()
colorized_new_filename = colorized_new_filename.lower()
# to UPPERCASE only
if args.uppercase and new_filename:
new_filename = filename.upper()
colorized_new_filename = colorized_new_filename.upper()
if not new_filename:
continue
# filepath_old = filename
# filepath_new = new_filename
# filepath_old = os.path.join(path, filename)
# filepath_new = os.path.join(path, new_filename)
# if args.lowercase:
# new_filename = new_filename.lower()
# if args.uppercase:
# new_filename = new_filename.upper()
if not new_filename:
print('Replacement expression "{}"" returns empty value! Skipping'.format(args.replace))
continue
else:
print(colorized_old_filename, '→', colorized_new_filename)
if args.write_changes:
new_path_abs = Path(filepath.parent.absolute()/new_filename)
if not new_path_abs.exists() or args.overwrite_existing:
filepath.rename(new_path_abs)
# shutil.move(filepath.absolute(), new_path_abs)
else:
print(f"WARNING: Target file already exists: {new_filename} - Refusing to overwrite.")
else:
if args.show_non_matching:
print('Name [{filename}] does not match search regex [{args.search}]')
def bracketize_regex(rx):
"""surround regex with brackets if needed"""
if rx.startswith('(') and rx.endswith(')'):
return rx
else:
return f'({rx})'
def is_required_arg(argz):
for key in ('-k', '-K', '--lowercase', '--uppercase'):
if key in argz:
return False
return True
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Recursive file name renaming with regex support')
parser.add_argument('root_folder',
help='Top folder for the replacement operation',
nargs='?',
action='store',
default='.')
parser.add_argument('-s', '--search',
help='string to search for',
action='store',
default=None,
required=is_required_arg(sys.argv))
parser.add_argument('-r', '--replace',
help='string to replace with',
action='store',
default=None,
required=is_required_arg(sys.argv))
parser.add_argument('-w', '--write-changes',
action='store_true',
help='Write changes to files (otherwise just simulate the operation) (DEFAULT: False)',
default=False)
parser.add_argument('-l', '--literal',
action='store_true',
help='Use literal text for search/replace (not regular expressions) (DEFAULT: False)',
default=False)
parser.add_argument('-y', '--overwrite_existing',
action='store_true',
help='Overwrite existing files',
default=False)
parser.add_argument('-g', '--glob',
action='store',
help='only process files matching glob (DEFAULT: "*.*")',
default="*.*")
parser.add_argument('-v', '--show-non-matching',
action='store_true',
help='Show files that do not match (DEFAULT: False)',
default=False)
parser.add_argument('-k', '--lowercase',
action='store_true',
help='Convert to lower case (DEFAULT: False)',
default=False)
parser.add_argument('-K', '--uppercase',
action='store_true',
help='Convert to UPPER CASE (DEFAULT: False)',
default=False)
args = parser.parse_args(sys.argv[1:])
print(args)
rename_files(args)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment