|
|
(16 intermediate revisions by 7 users not shown) |
Line 1: |
Line 1: |
− | [[Category:Scripts (English)]] | + | [[Category:Scripts]] |
− | [[Category:Package management (English)]] | + | [[Category:Package management]] |
| + | [[zh-CN:CacheClean]] |
| + | Cacheclean is python script to clean {{ic|/var/cache/pacman/pkg}} allowing users to specify how many package versions should be retained. In function, it is similar to {{ic|pacman -Sc}} except users select how many old versions to keep. Another difference is unlike, {{ic|pacman -Sc}} cacheclean does not discriminate against packages that are not currently installed as it inspects {{ic|/var/cache/pacman/pkg}}. |
| | | |
− | This script has been included in the AUR at [[http://aur.archlinux.org/packages.php?ID=37572 this page]].
| + | == Installation == |
| + | [[AUR]] package: {{AUR|cacheclean}} |
| | | |
− | It was originally written by "alterkacker" - see his forum thread: [[http://bbs.archlinux.org/viewtopic.php?id=9104||A utility for cleaning /var/cache/pacman/pkg]]
| + | == Usage == |
| | | |
| + | {{bc|cacheclean {-p} {-v} <# of copies to keep> |
| + | # of copies to keep - (required) how many generations of each package to keep |
| + | -p - (optional) preview what would be deleted; forces verbose (-v) mode. |
| + | -v - (optional) show deleted packages.}} |
| | | |
| + | == Code == |
| + | For those wishing to contribute, the script's source code is hosted in [https://github.com/graysky2/cacheclean/tree/master/cacheclean this github repository]. To simplify updates, please fork and send a pull request with updates/changes. |
| | | |
− | <pre>
| + | == See also == |
− | #!/usr/bin/env python
| + | *Author's original forum thread: [http://bbs.archlinux.org/viewtopic.php?id=9104 A utility for cleaning /var/cache/pacman/pkg] |
− | """cache_clean - a simple python script to clean up the /var/cache/pacman/pkg directory.
| |
− | More versatile than 'pacman -Sc' in that you can select how many old versions
| |
− | to keep.
| |
− | Usage: cache_clean {-p} {-v} <# of copies to keep>
| |
− | # of copies to keep - (required) how many generations of each package to keep
| |
− | -p - (optional) preview what would be deleted; forces verbose (-v) mode.
| |
− | -v - (optional) show deleted packages."""
| |
− | # Note that the determination of package age is done by simply looking at the date-time
| |
− | # modified stamp on the file. There is just enough variation in the way package version
| |
− | # is done that I thought this would be simpler & just as good.
| |
− | # Also note that you must be root to run this script.
| |
− | | |
− | import getopt
| |
− | import os
| |
− | import re
| |
− | import sys
| |
− | | |
− | # helper function to get tuple of (file dtm, file name, file sz)
| |
− | def fn_stats(fn):
| |
− | s = os.stat(fn)
| |
− | return (s.st_mtime, fn, s.st_size)
| |
− | | |
− | # cleanup does the actual pkg file deletion
| |
− | def cleanup(run_list):
| |
− | # strictly speaking only the first two of these globals need to be listed.
| |
− | global n_deleted, bytes_deleted, opt_verbose, opt_preview, n_to_keep
| |
− | # return if the run_list is too short
| |
− | #print run_list
| |
− | #return
| |
− | if len(run_list) <= n_to_keep: return
| |
− | # Build list of tuples (date-time file modified, file name, file size)
| |
− | dtm_list = [fn_stats(tfn) for tfn in run_list]
| |
− | # Sort the list by date-time
| |
− | dtm_list.sort()
| |
− | # Build list of the filenames to delete (all but last n_to_keep).
| |
− | # <showing_off>
| |
− | #kill_list = [tfn[1] for tfn in dtm_list[:-n_to_keep]]
| |
− | #bytes_deleted = sum(x[2] for x in dtm_list[:-n_to_keep])
| |
− | # </showing_off>
| |
− | kill_list = []
| |
− | for x in dtm_list[:-n_to_keep]:
| |
− | kill_list.append(x[1])
| |
− | bytes_deleted += x[2]
| |
− | if opt_verbose: print kill_list
| |
− | n_deleted += len(kill_list)
| |
− | # and finally delete (if not in preview mode)
| |
− | if not opt_preview:
| |
− | for dfn in kill_list:
| |
− | os.unlink(dfn)
| |
− | | |
− | ######################################################################
| |
− | # mainline processing
| |
− | | |
− | # process command line options
| |
− | try:
| |
− | opts, pargs = getopt.getopt(sys.argv[1:], 'vp')
| |
− | opt_dict = dict(opts)
| |
− | opt_preview = '-p' in opt_dict
| |
− | opt_verbose = '-v' in opt_dict
| |
− | if opt_preview: opt_verbose = True
| |
− | if len(pargs) == 1:
| |
− | n_to_keep = pargs[0]
| |
− | else:
| |
− | raise getopt.GetoptError("missing required argument.")
| |
− | try:
| |
− | n_to_keep = int(n_to_keep)
| |
− | if n_to_keep <= 0: raise ValueError
| |
− | except ValueError, e:
| |
− | raise getopt.GetoptError("# of copies to keep must be numeric > 0!")
| |
− | except getopt.GetoptError, msg:
| |
− | print "Error:",msg,"\n",__doc__
| |
− | sys.exit(1)
| |
− | | |
− | # change to the pkg directory & get a sorted list of its contents
| |
− | os.chdir('/var/cache/pacman/pkg')
| |
− | pkg_fns = os.listdir('.')
| |
− | pkg_fns.sort()
| |
− | | |
− | # Pattern to use to extract the package name from the tar file name:
| |
− | # for pkg e.g. 'gnome-common-2.8.0-1-i686.pkg.tar.gz' group(1) is 'gnome-common'.
| |
− | bpat = re.compile(r'^(.+?)(?:-\d.*?)(-i686|-x86_64|-any)?\.pkg\.tar\.(?:gz|xz)$')
| |
− | | |
− | n_deleted = 0
| |
− | bytes_deleted = 0
| |
− | pkg_base_nm = ''
| |
− | # now look for "runs" of the same package name differing only in version info.
| |
− | for run_end in range(len(pkg_fns)):
| |
− | fn = pkg_fns[run_end]
| |
− | | |
− | # make sure we skip directories
| |
− | if not os.path.isdir(fn):
| |
− | mo = bpat.match(fn) # test for a match of the package name pattern
| |
− | if mo:
| |
− | tbase = mo.group(1) # gets the 'base' package name
| |
− | # include the architecture in the name if it's present
| |
− | if mo.lastindex > 1:
| |
− | tbase += mo.group(2)
| |
− | #print 'tbase: ',tbase,' ',mo.lastindex
| |
− | # is it a new base name, i.e. the start of a new run?
| |
− | if tbase != pkg_base_nm: # if so then process the prior run
| |
− | if pkg_base_nm != '':
| |
− | cleanup(pkg_fns[run_start:run_end])
| |
− | pkg_base_nm = tbase # & setup for the new run
| |
− | run_start = run_end
| |
− | else:
| |
− | print >>sys.stderr, "File '"+fn+"' doesn't match package pattern!"
| |
− | else:
| |
− | print >>sys.stdout, "skipping directory '"+fn+"'!"
| |
− | | |
− | # catch the final run of the list
| |
− | run_end += 1
| |
− | cleanup(pkg_fns[run_start:run_end])
| |
− | | |
− | if opt_verbose:
| |
− | if opt_preview:
| |
− | print "Preview mode (no files deleted):",
| |
− | print n_deleted,"files deleted,",bytes_deleted/1000,"kbytes."
| |
− | </pre>
| |