File indexing completed on 2024-06-23 04:15:19
0001 #!/usr/bin/env python3 0002 0003 # Generic search & replace tool 0004 # https://github.com/aureliojargas/replace 0005 # 0006 # SPDX-FileCopyrightText: 2016 Aurelio Jargas <aurelio at aurelio dot net> 0007 # 0008 # SPDX-License-Identifier: BSD-3-Clause 0009 # 0010 0011 """ 0012 Replaces text using string or regex matching. 0013 0014 Examples: 0015 # Replace all mentions of old.css with new.css in all HTML files 0016 replace --from old.css --to new.css --in-place *.html 0017 0018 # Update the AdSense code in all HTML files 0019 # The old and the new code are in separate files 0020 replace --from-file adsense.old --to-file adsense.new -i *.html 0021 0022 # Enclose all numbers inside square brackets: 123 -> [123] 0023 replace --regex --from '(\\d+)' --to '[\\1]' file.txt 0024 """ 0025 0026 import argparse 0027 import re 0028 import sys 0029 0030 0031 def read_file(path): 0032 if path == "-": 0033 return sys.stdin.read() 0034 # The newline argument preserves the original line break (see issue #2) 0035 with open(path, "r", newline="", encoding="utf-8") as myfile: 0036 return myfile.read() 0037 0038 0039 def save_file(path, content): 0040 with open(path, "w", encoding="utf-8") as myfile: 0041 myfile.write(content) 0042 0043 0044 def setup_cmdline_parser(): 0045 parser = argparse.ArgumentParser( 0046 description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter 0047 ) 0048 0049 # from 0050 group = parser.add_mutually_exclusive_group() 0051 group.add_argument( 0052 "-f", 0053 "--from", 0054 metavar="TEXT", 0055 dest="from_", 0056 help="specify the search text or regex", 0057 ) 0058 group.add_argument( 0059 "-F", "--from-file", metavar="FILE", help="read the search text from this file" 0060 ) 0061 0062 # to 0063 group = parser.add_mutually_exclusive_group() 0064 group.add_argument( 0065 "-t", "--to", metavar="TEXT", help="specify the replacement text" 0066 ) 0067 group.add_argument( 0068 "-T", 0069 "--to-file", 0070 metavar="FILE", 0071 help="read the replacement text from this file", 0072 ) 0073 0074 # other 0075 parser.add_argument( 0076 "-r", 0077 "--regex", 0078 action="store_true", 0079 help="use regex matching instead of string matching", 0080 ) 0081 parser.add_argument( 0082 "-i", "--in-place", action="store_true", help="edit files in-place" 0083 ) 0084 parser.add_argument( 0085 "-v", "--verbose", action="store_true", help="turn on verbose mode" 0086 ) 0087 0088 # files 0089 parser.add_argument("files", metavar="FILE", nargs="+", help="input files") 0090 return parser 0091 0092 0093 def validate_config(config): 0094 # Set search pattern 0095 if config.from_file: 0096 config.from_value = read_file(config.from_file) 0097 elif config.from_: 0098 config.from_value = config.from_ 0099 else: 0100 sys.exit("Error: No search pattern (use --from or --from-file)") 0101 0102 # Set replacement 0103 if config.to_file: 0104 config.to_value = read_file(config.to_file) 0105 elif config.to is not None: # could also be '' 0106 config.to_value = config.to 0107 else: 0108 sys.exit("Error: No replace pattern (use --to or --to-file)") 0109 0110 0111 def main(args=None): 0112 parser = setup_cmdline_parser() 0113 config = parser.parse_args(args) 0114 validate_config(config) 0115 0116 from_ = config.from_value 0117 to_ = config.to_value 0118 0119 for input_file in config.files: 0120 0121 if config.verbose: 0122 print("----", input_file) 0123 0124 original = read_file(input_file) 0125 0126 # do the replace 0127 if config.regex: 0128 modified = re.sub(from_, to_, original) 0129 else: 0130 modified = original.replace(from_, to_) 0131 0132 # save or show results 0133 if config.in_place: 0134 if modified == original: 0135 continue # do not save unchanged files 0136 save_file(input_file, modified) 0137 print("Saved", input_file) 0138 else: 0139 print(modified, end="") 0140 0141 0142 if __name__ == "__main__": 0143 main()