Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

cache_clean.py

By: a guest on Oct 20th, 2010  |  syntax: Python  |  size: 4.35 KB  |  views: 66  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. #!/usr/bin/env python
  2. """cache_clean - a simple python script to clean up the /var/cache/pacman/pkg directory.
  3. More versatile than 'pacman -Sc' in that you can select how many old versions
  4. to keep.
  5. Usage: cache_clean {-p} {-v} <# of copies to keep>
  6.  # of copies to keep - (required) how many generations of each package to keep
  7.  -p - (optional) preview what would be deleted; forces verbose (-v) mode.
  8.  -v - (optional) show deleted packages."""
  9. # Note that the determination of package age is done by simply looking at the date-time
  10. # modified stamp on the file. There is just enough variation in the way package version
  11. # is done that I thought this would be simpler & just as good.
  12. # Also note that you must be root to run this script.
  13.  
  14. import getopt
  15. import os
  16. import re
  17. import sys
  18.  
  19. # helper function to get tuple of (file dtm, file name, file sz)    
  20. def fn_stats(fn):
  21.   s = os.stat(fn)
  22.   return (s[8], fn, s[6])
  23.  
  24. # cleanup does the actual pkg file deletion
  25. def cleanup(run_list):
  26.   # strictly speaking only the first two of these globals need to be listed.
  27.   global n_deleted, bytes_deleted, opt_verbose, opt_preview, n_to_keep
  28.   # return if the run_list is too short
  29.   #print run_list
  30.   #return
  31.   if len(run_list) <= n_to_keep: return
  32.   # Build list of tuples (date-time file modified, file name, file size)
  33.   dtm_list = [fn_stats(tfn) for tfn in run_list]
  34.   # Sort the list by date-time
  35.   dtm_list.sort()
  36.   # Build list of the filenames to delete (all but last n_to_keep).
  37.   # <showing_off>
  38.   #kill_list = [tfn[1] for tfn in dtm_list[:-n_to_keep]]
  39.   #bytes_deleted = sum(x[2] for x in dtm_list[:-n_to_keep])
  40.   # </showing_off>
  41.   kill_list = []
  42.   for x in dtm_list[:-n_to_keep]:
  43.     kill_list.append(x[1])
  44.     bytes_deleted += x[2]
  45.   if opt_verbose: print (kill_list)
  46.   n_deleted += len(kill_list)
  47.   # and finally delete (if not in preview mode)
  48.   if not opt_preview:
  49.     for dfn in kill_list:
  50.       os.unlink(dfn)
  51.  
  52. ######################################################################
  53. # mainline processing
  54.  
  55. # process command line options
  56. try:
  57.   opts, pargs = getopt.getopt(sys.argv[1:], 'vp')
  58.   opt_dict = dict(opts)
  59.   opt_preview = '-p' in opt_dict
  60.   opt_verbose = '-v' in opt_dict
  61.   if opt_preview: opt_verbose = True
  62.   if len(pargs) == 1:
  63.     n_to_keep = pargs[0]
  64.   else:
  65.     raise getopt.GetoptError("missing required argument.")
  66.   try:
  67.     n_to_keep = int(n_to_keep)
  68.     if n_to_keep <= 0: raise ValueError
  69.   except ValueError as e:
  70.     raise getopt.GetoptError("# of copies to keep must be numeric > 0!")
  71. except getopt.GetoptError as msg:
  72.   print ("Error:",msg,"\n",__doc__)
  73.   sys.exit(1)
  74.  
  75. # change to the pkg directory & get a sorted list of its contents
  76. os.chdir('/var/cache/pacman/pkg')
  77. pkg_fns = os.listdir('.')
  78. pkg_fns.sort()
  79.  
  80. # Pattern to use to extract the package name from the tar file name:
  81. # for pkg e.g. 'gnome-common-2.8.0-1-i686.pkg.tar.gz' group(1) is 'gnome-common'.
  82. bpat = re.compile(r'^(.+)-\d[^-]+-.+?(-i686|-x86_64|-any)?\.pkg\.tar\.(gz|bz2|xz)(\.aria2)?$')
  83.  
  84. n_deleted = 0
  85. bytes_deleted = 0
  86. pkg_base_nm = ''
  87. # now look for "runs" of the same package name differing only in version info.
  88. for run_end in range(len(pkg_fns)):
  89.   fn = pkg_fns[run_end]
  90.  
  91.   # make sure we skip directories
  92.   if os.path.isdir(fn) == False:
  93.     mo = bpat.match(fn) # test for a match of the package name pattern
  94.     if mo:
  95.       #print >>sys.stdout, "Processing file '" + fn + "'", mo.lastindex
  96.       tbase = mo.group(1) # gets the 'base' package name
  97.       # include the architecture in the name if it's present
  98.       if mo.lastindex > 1:
  99.         if mo.group(2) is not None:
  100.             tbase += mo.group(2)
  101.       #print 'tbase: ',tbase,'  ',mo.lastindex
  102.       # is it a new base name, i.e. the start of a new run?
  103.       if tbase != pkg_base_nm: # if so then process the prior run
  104.         if pkg_base_nm != '':
  105.           cleanup(pkg_fns[run_start:run_end])
  106.         pkg_base_nm = tbase # & setup for the new run
  107.         run_start = run_end
  108.     else:
  109.       print ("File '"+fn+"' doesn't match package pattern!", file=sys.stderr)
  110.   else:
  111.     print >>sys.stdout, "skipping directory '"+fn+"'!"
  112.  
  113. # catch the final run of the list
  114. run_end += 1
  115. cleanup(pkg_fns[run_start:run_end])
  116.  
  117. if opt_verbose:
  118.   if opt_preview:
  119.     print ("Preview mode (no files deleted):"),
  120.   print (n_deleted,"files deleted,",bytes_deleted/1000,"kbytes.")