#!/bin/python3.9 import re import hashlib import os from sys import argv, stderr from os.path import basename from colorama import init, Fore, Back, Style init(autoreset=True) if len(argv) <= 3: print('usage: {0} [REGEX] [SUB] [FILES...]'.format(basename(__file__)), file=stderr) exit(1) regex = argv[1] sub = argv[2] files = argv[3:] replace_all = False quit_loop = False sub_count = 0 match_count = 0 def prompt(matchobj, line, line_n, file): global replace_all global quit_loop global sub_count global match_count if quit_loop: return matchobj.group(0) replaced_match = re.sub(regex, sub, matchobj.group(0)) if replaced_match == matchobj.group(0): return replaced_match match_count += 1 if replace_all: sub_count += 1 return replaced_match highlighted = line.replace(matchobj.group(0), Back.RED + matchobj.group(0) + Back.RESET) replaced = line.replace(matchobj.group(0), Back.YELLOW + Fore.BLACK + replaced_match + Back.RESET + Fore.RESET) print(Fore.GREEN + Style.BRIGHT + file) print(Fore.YELLOW + Style.BRIGHT + str(line_n), end='') print(':{0}'.format(highlighted)) print('Becomes the following:') print(Fore.YELLOW + Style.BRIGHT + str(line_n), end='') print(':{0}'.format(replaced)) print() while True: answer = input('Confirm the change? [Y/n/a/q] ').lower() if answer in ['y', 'yes', '']: sub_count += 1 print() return replaced_match elif answer in ['n', 'no']: print() return matchobj.group(0) elif answer in ['a', 'all']: sub_count += 1 replace_all = True return replaced_match elif answer in ['q', 'quit']: quit_loop = True return matchobj.group(0) print('Invalid answer. Please type again.') def filter_file(fname): with open(fname, 'r') as file: contents = file.read() lines = contents.splitlines() lines = [ re.sub(regex, lambda matchobj: prompt(matchobj, line, line_n, fname), line) for (line_n, line) in enumerate(lines) ] new_contents = '\n'.join(lines) hash_old = hashlib.md5(contents.encode()) hash_new = hashlib.md5(new_contents.encode()) if hash_old.digest() != hash_new.digest(): with open(fname, 'w') as file: file.write(new_contents) file.write('\n') for file in files: if os.access(file, os.W_OK): filter_file(file) if quit_loop: break if match_count == 0: print(Fore.RED + Style.BRIGHT + 'No matches found.') else: print(Fore.YELLOW + Style.BRIGHT + 'Made {0} of {1} substitutions.'. format(Fore.WHITE + str(sub_count) + Fore.YELLOW, Fore.WHITE + str(match_count) + Fore.YELLOW))