package cleanup script
I have written a (useful?) script to find all packages which have not be
used for a long time and reports this to the user (i.e., some package is
installed but none of its files have been accessed in 60 days). Would
this be useful? Is there some other forum where I should post this?
I have attached the script for consideration. The only requirements are
ruby-1.8 and grep-dctrl.
-Dale Hawkins
#!/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
}
Reply to: