file-expire - 特定のディレクトリ内の古いファイルを削除するスクリプト

最近ディスク容量がきつきつになってきているので、古いファイルを消したり動画をDVDに島流ししたりでなんとか空容量を工面している状況だ。
店員さんに「ハードディスクはしょせん消耗品、3年持たないものだ」と言われた80GBのハードディスクも稼動して3年半になる…逝きそうな気配はまったくないが、寿命は近いだろう。せっかく長生きしてくれているんだから最後の瞬間まで使ってあげようと思う。待てば待つほどハードディスクは安くなるんだから。

知らない間にプログラムのログファイルやキャッシュファイルが溜っていって、ディスクを埋め尽くすことはないだろうか?古いファイルはたまに掃除してあげると空容量が確保できる。だが、ディスクの掃除を手作業でやるとかなり面倒なので自動化してしまうのが一番。そこでこのスクリプトを。activesupportを使っているのでgem install activesupportしよう。railsの人は不要。

#!/usr/bin/env ruby
require 'rubygems'
require 'active_support/core_ext/numeric'
require 'fileutils'
$files_to_expire = []

################ API
def file(secs_before, *globs)
  _globs(globs) do |file|
    stat = File.stat(file)
    next if stat.directory?
    $files_to_expire << [file, stat.mtime, :F] if stat.mtime < secs_before.ago
  end
end

def directory(secs_before, *globs)
  _globs(globs) do |file|
    stat = File.stat(file)
    $files_to_expire << [file, stat.mtime, :D] if stat.directory? and stat.mtime < secs_before.ago
  end
end
################ /API
def _globs(globs)
  globs.each do |glob|
    Dir[File.expand_path(glob)].each do |file|
      begin
        yield(file)
      rescue
      end
    end
  end
end

def show_expires(expire_files=$files_to_expire)
  expire_files.each do |file, mtime, f_or_d|
    puts "#{f_or_d} #{mtime}  #{file}"
  end
end

def delete_expires(expire_files=$files_to_expire)
  expire_files.each do |file, mtime, f_or_d|
    puts "#{f_or_d} #{mtime}  #{file} (remove)"
    begin
      if f_or_d == :F
        FileUtils.rm_f file
      else
        FileUtils.rm_rf file
      end
    rescue
      $stderr.puts "Failed to unlink #{file}."
    end
  end
end

require 'optparse'
@func = :show_expires
@config = File.expand_path "~/.file-expire.rb"
ARGV.options {|o|
  o.on("-d", "Delete old files.") {|x| @func = :delete_expires }
  o.on("-c CONFIG", "--config=CONFIG", "Set config file instead of default one.") {
|x| @config = x  }
  o.parse!
}

begin
  load @config
rescue LoadError
  $stderr.puts "#@config does not exist or has error.", $!
end

__send__ @func

このスクリプトは設定ファイル ~/.file-expire.rb に書かれた指示に基いて古いファイルやディレクトリを消していく。~/.file-expire.rbはれっきとしたRubyスクリプトで2種類の指示を出せる。

file 期限, 消すファイルのパターン...
directory 期限, 消すディレクトリのパターン...

消すファイルのパターンはDir[]で指定できるワイルドカードで複数指定できる。どちらも期限よりも古いファイルやディレクトリを消す。fileはファイルのみ削除し、directoryはディレクトリ内のファイルをもまるごと消す。

たとえば以下の指示は、1年以上前の /log/palm-backup/db 内のディレクトリすべてと、1週間以上前の w3m の一時ファイルを削除する設定だ。数字の後ろにはrailsでおなじみの時間単位のメソッドをつけられる。(activesupportの機能)

directory 1.year, "/log/palm-backup/db/*"
file 1.week, "~/.w3m/w3mtmp*", "~/.w3m/w3mcache*", "~/.w3m/w3msrc*"

誤動作を防ぐため、引数なしで起動した場合はファイルを消さずに表示するのみである。消すには-dオプションをつけること。

設定ファイルに書いているのは、たんなる宣言に過ぎない。宣言を読み込むと該当するファイルたちが配列に溜まっていく。Rubyは気楽にDSLが作れていいものだ。