Skip to content

Instantly share code, notes, and snippets.

@noscript
Last active March 22, 2020 17:11
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save noscript/29a150a4225087219b90 to your computer and use it in GitHub Desktop.
Dpkg Time Machine
#!/usr/bin/env python3
# file: dpkg-since.py
# description: lists installed packages since the specified date
# changelog:
# * Feb 10 2015 - init release
# * Mar 24 2015 - new: packages sorted by installation time/date
# fix: ignored TIME argument
# * Apr 9 2015 - fix: exclude duplicates
# * Apr 27 2015 - new: recognize dates like '1 hour ago', 'yesterday'
# requires 'dateparser'
# - new: use new line or other splitter character
# * Jul 27 2019 - new: upgraded to python 3
# * Feb 03 2020 - fix: clean up
# * Mar 22 2020 - change: removed 'dateparser' dependency, performance fixes
import sys
import getopt
from os.path import basename
from datetime import datetime
from glob import glob
import gzip
import re
import subprocess
def uniquify(seq):
seen = set()
seen_add = seen.add
return [x for x in seq if x not in seen and not seen_add(x)]
def print_usage():
basename_ = basename(sys.argv[0])
print('')
print('Usage:\n\t' + basename_ + ' [OPTIONS] DATE')
print('')
print('Options:')
print('\t-p, --print-date - prints the recognized date as the last line')
print("\t-n, --new-line - use new line as a splitter character")
print("\t-s, --splitter 'char' - set a custom splitter character")
print('\t-h, --help - display this help and exit')
print('')
print('Examples:')
print('\t' + basename_ + ' -p yesterday')
print('\t' + basename_ + ' -n 1 week ago')
print('\t' + basename_ + " -s ', ' 2015-02-14 16:40:35")
def main():
print_date = False
splitter = " "
try:
opts, args = getopt.getopt(sys.argv[1:], "hps:n", ["help", "print-date", "splitter=", "new-line"])
except getopt.GetoptError as err:
print(str(err))
print_usage()
sys.exit(2)
for o, a in opts:
if o in ("-h", "--help"):
print_usage()
sys.exit()
elif o in ("-p", "--print-date"):
print_date = True
elif o in ("-s", "--splitter"):
splitter = a
if splitter == "\\n":
splitter = "\n"
elif o in ("-n", "--new-line"):
splitter = "\n"
else:
assert False, "invalid option"
if (not args):
print('missing date arguments')
print_usage()
sys.exit(1)
date_pattern = re.compile(r'(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})')
since_date_str = subprocess.check_output(['date', '-d', ' '.join(args), '+"%Y-%m-%d %H:%M:%S"']).decode()[1:-2]
since_date = datetime(*map(int, date_pattern.match(since_date_str).groups()))
packages = []
for log in sorted(glob('/var/log/dpkg.log*'), reverse=True):
if log.endswith('.gz'):
with gzip.open(log, 'rt') as f:
lines = f.read().splitlines()
else:
with open(log, 'r') as f:
lines = f.read().splitlines()
for line in lines:
cols = line.split()
line_date = datetime(*map(int, date_pattern.match(' '.join(cols[:2])).groups()))
if line_date >= since_date:
# keep only installed ones
if cols[2] == 'install':
packages.append(cols[3])
print(splitter.join(uniquify(packages)))
if print_date:
print("\n" + since_date.ctime() + "")
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment