3 # Copyright (c) 2020 Cisco and/or its affiliates.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 """A script simplifying replacement of blocks of lines.
18 A bash solution created by combining these two:
19 https://unix.stackexchange.com/a/181215
20 https://stackoverflow.com/a/23849180
21 does not seem to work if the blocks contain complicated characters.
29 """Main function for the block replacing script."""
31 description = '''Replace a block of lines with another block.
33 Both block-to-replace and replacing-block are read from a file.
34 The replacement is performed on a file, in-place.
35 Only first block occurence is replaced.
36 If the block-to-replace is preceded by a partial match,
37 it may not be recognized.
39 The current implementation uses temporary files,
40 created in the working directory.
41 if something fails, thise temporary files need to be deleted manually.
43 TODO: Preserve target attributes. Maybe https://pypi.org/project/in-place/
45 parser = argparse.ArgumentParser(description)
48 help=u"Path to file containing the old content to replace."
52 help=u"Path to file containing the new content to replace with."
55 u"targets", metavar=u"target", nargs=u"+", type=str,
56 help=u"Paths to file where the replacement should be made."
58 args = parser.parse_args()
64 """Read contents, create edited target, replace the original target with it.
66 :param args: Parsed command line arguments.
67 :type args: Object (typically argparse.Namespace) which contains
68 "before", "after" and "target" fields.
70 with open(args.before, u"r") as file_in:
71 content_before = file_in.readlines()
72 before_len = len(content_before)
73 with open(args.after, u"r") as file_in:
74 content_after = file_in.readlines()
76 for target in args.targets:
77 with tempfile.NamedTemporaryFile(
78 dir=u".", mode=u"w", delete=False
80 with open(target, u"r") as file_in:
81 # Phase one, searching for content, copying what does not match.
83 line_index_to_check = 0
86 line_in = file_in.readline()
88 print(f"{target}: Content not found.")
89 for line_out in buffer_lines:
90 file_out.write(line_out)
93 if line_in != content_before[line_index_to_check]:
94 line_index_to_check = 0
96 for line_out in buffer_lines:
97 file_out.write(line_out)
99 file_out.write(line_in)
101 buffer_lines.append(line_in)
102 line_index_to_check += 1
103 if line_index_to_check < before_len:
105 # Buffer has the match! Do not write it.
108 if not content_found:
110 os.remove(file_out.name)
112 # Phase two, write the replacement instead.
113 for line_out in content_after:
114 file_out.write(line_out)
115 # Phase three, copy the rest of the file.
117 line_in = file_in.readline()
119 print(f"{target}: Replacement done.")
121 file_out.write(line_in)
122 os.replace(file_out.name, target)
125 if __name__ == u"__main__":