[CLUE-Tech] Useful debian cleanup script?
Dale K. Hawkins
dhawkins at cdrgts.com
Thu May 27 17:42:04 MDT 2004
I noticed there are some Debian users on the list. I wrote a utility
that can help determine packages which are never used (not accessed) and
recommend them for cleanup.
It is not perfect, but it is useful. I was hoping to get some feedback
on the script and then release it to the world.
You need ruby1.8 installed and grep-dctrl.
The general idea is to feed a list of package names to the program (via
the command-line or all packages are considered by default) and the
program spits out a list of packages which have not been accessed for
some number of days (60 is the default but see the -d option).
You can than feed that list back to 'apt-get remove' albeit cautiously.
Possible enhancements include a recursive depends feature.
This will not erase any files!
-Dale
-------------- next part --------------
#!/usr/bin/ruby
require 'pathname'
require 'getoptlong'
def usage
$stderr.puts "usage: #{$0} [ -v ]* [ -r ] [ -d days(60) ] <pkg_name> [ ... ]"
exit 1
end
days = 60
verbose = 0
recursive = false
opts = GetoptLong.new(["--days", "-d", GetoptLong::REQUIRED_ARGUMENT],
["--verbose", "-v", GetoptLong::NO_ARGUMENT],
["--recursive", "-r", GetoptLong::NO_ARGUMENT],
["--help", "-h", GetoptLong::NO_ARGUMENT])
opts.each {|opt,val|
case opt
when "--days" then days = val
when "--verbose" then verbose += 1
# when "--recursive" then recursive = true
else
usage
end
}
if days.to_i == 0
$stderr.puts "illegal value given for days: #{days}"
end
days = days.to_i
if ARGV.empty?
packages = IO.popen('grep-status -FStatus -sPackage -n "install ok installed"').readlines
else
packages = ARGV
end
class Pathname
@@secondsPerDay = 60 * 60 * 24
def parentInArray(paths)
paths.any? { | p |
fnmatch(p)
}
end
def lastAccessed(t = Time.now)
((t - self.atime)/@@secondsPerDay).to_i
end
end
class Package
@@filesToIgnore = [
'/usr/lib/menu/*',
'/usr/share/applications/*',
'/usr/share/man/*',
'/usr/share/pixmaps/*',
'/usr/share/mime-info/*',
'/usr/lib/mime/packages/*',
'/usr/share/doc-base/*',
]
@@docDirs = [
'/usr/share/doc/*',
]
@@binaryDirs = [
'/lib/*',
'/usr/lib/*',
'/bin/*',
'/usr/bin/*',
]
def initialize(name)
@name = name
end
def getFiles
if not defined? @files
@files = IO.popen("grep-dctrl -FStatus ' installed' /var/lib/dpkg/status | grep-dctrl -PX -n -s Package #{@name}").collect { | pkg |
IO.popen("dpkg --listfiles #{pkg}").collect { | fn |
fn.chomp!
if fn.empty?
false
else
p = Pathname.new(fn.chomp)
end
}.select { | p |
p && (p.file? && (! p.parentInArray(@@filesToIgnore)))
}
}.flatten
end
if @files.empty?
raise "No regular files found (or package not installed)"
end
@files
end
def docFiles
if not defined? @docFiles
@docFiles = getFiles.select { | p |
p.parentInArray(@@docDirs)
}.sort {|x,y| y.atime <=> x.atime }
end
@docFiles
end
def binaryFiles
if not defined? @binFiles
@binFiles = getFiles.select { | p |
p.parentInArray(@@binaryDirs)
}.sort {|x,y| y.atime <=> x.atime }
end
@binFiles
end
def lastDocAccess
if docFiles.empty?
raise "No document files"
else
docFiles[0].lastAccessed
end
end
def showlastDocumentAccess(number = 1)
if docFiles.empty?
raise "No doc files"
else
access_times = (0...([number, docFiles.size].min)).collect { | n |
"#{docFiles[n]} was last accessed #{docFiles[n].lastAccessed} days ago"
}
end
end
def lastBinaryAccess
if binaryFiles.empty?
raise "No binary files"
else
binaryFiles[0].lastAccessed
end
end
def showlastBinaryAccess(number = 1)
if binaryFiles.empty?
raise "No binary files"
else
access_times = (0...([number, binaryFiles.size].min)).collect { | n |
"#{binaryFiles[n]} was last accessed #{binaryFiles[n].lastAccessed} days ago"
}
end
end
def getWhatDeps
IO.popen("grep-status -n -s Package -FDepends -e #{@name}").readlines
end
end
packages.each { | pn |
pn.chomp!
next if pn.empty?
if verbose > 0
puts "=============== #{pn} ==============="
end
success = false
p = Package.new(pn)
binDays = 10000
begin
binDays = p.lastBinaryAccess
if verbose > 1
puts p.showlastBinaryAccess(10).collect { | s | " " + s }
end
success = true
rescue
if verbose > 0
$stderr.puts $!
end
end
docDays = 10000
begin
docDays = p.lastDocAccess
if verbose > 1
puts p.showlastDocumentAccess(10).collect { | s | " " + s }
end
success = true
rescue
if verbose > 0
$stderr.puts $!
end
end
if success
if [binDays, docDays].min > days
puts "Recommend #{pn} for removal"
if recursive
puts p.getWhatDeps
end
else
if verbose > 0
puts "Keeping #{pn}"
end
end
end
}
More information about the clue-tech
mailing list