Skip to content

Instantly share code, notes, and snippets.

@wecsam
Last active February 4, 2019 02:08
Show Gist options
  • Save wecsam/ffecb8066819f77ca0e789a2c39dc65e to your computer and use it in GitHub Desktop.
Save wecsam/ffecb8066819f77ca0e789a2c39dc65e to your computer and use it in GitHub Desktop.
This tool moves all but the last N log entries from the specified file to another file.
#!/usr/bin/env python3
import argparse, collections, os.path, sys
def move_lines(fin, fout, lines_to_leave, preserve_first_line):
# For CSV files, preserve the first line, which is the header row.
if preserve_first_line:
first_line = next(fin)
else:
first_line = ""
# If the output file is new, write the first line into it.
if fout.tell() == 0:
fout.write(first_line)
# Maintain a circular buffer for the last N entries from the original log.
buffer = collections.deque()
# Fill the buffer with N lines.
for line, _ in zip(fin, range(lines_to_leave)):
buffer.append(line)
# Continue to fill the buffer, but write the oldest line to the archive in
# each iteration of the loop.
for line in fin:
buffer.append(line)
fout.write(buffer.popleft())
# The buffer now contains the last N entries from the original log.
# Write those back into the original log.
fin.seek(0)
fin.truncate()
fin.write(first_line)
for line in buffer:
fin.write(line)
def main(argv):
# Parse the command-line arguments.
arg_parser = argparse.ArgumentParser(
description=
"This tool moves all but the last N log entries from the "
"specified file to another file with \" - Archive\" appended to "
"the name (unless the output path is overridden). If the "
"extension is .CSV, then the header row is preserved in the "
"original and copied to the archive."
)
arg_parser.add_argument("file", help="The log file")
arg_parser.add_argument(
"-n",
"--lines-to-leave",
metavar="N",
type=int,
default=1000,
help="The number of lines to leave in the original log file"
)
arg_parser.add_argument(
"-o",
"--out-file",
help="Specify this argument to override the output path."
)
parsed_args = arg_parser.parse_args(argv[1:])
if parsed_args.lines_to_leave < 0:
arg_parser.error("The number of lines must not be negative.")
# Add " - Archive" to the end of the filename unless the output path was
# specified, in which case just use the specified output path.
path_without_extension, extension = os.path.splitext(parsed_args.file)
if parsed_args.out_file is None:
path_in = path_without_extension + extension
path_out = path_without_extension + " - Archive" + extension
else:
path_in = parsed_args.file
path_out = parsed_args.out_file
print("Source:", path_in)
print("Destination:", path_out)
# Check whether this is a CSV file.
is_csv = extension.upper() == ".CSV"
print("This", "is" if is_csv else "is not", "a CSV file.")
# Try to open the two files.
try:
fin = open(path_in, "r+", encoding="UTF-8")
except OSError as e:
arg_parser.error("Could not open original log file: " + str(e))
try:
fout = open(path_out, "a", encoding="UTF-8")
except OSError as e:
arg_parser.error("Could not open archive file: " + str(e))
# Move all but the last N log entries to the archive file.
with fin, fout:
move_lines(fin, fout, parsed_args.lines_to_leave, is_csv)
print("Done.")
if __name__ == "__main__":
# I have main() accept any list of arguments to make this easier to call
# from another Python script or from the interactive interpreter.
main(sys.argv)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment