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()