Ruby で各種ファイルのバックアップを行う

Ubuntu 9.04 に引っ越した時、設定ファイルのコピーを忘れたのでそれ用のスクリプトruby で作成した。コピー対象のファイルやディレクトリのリストは入力ファイルから読み込むようにした。日/週単位とかログとか用途別に入力ファイルを用意してみようかなと思っている。

ちなみに名前は bacon にしてみた。(BAckup CONfig file だから、朝作ったから…。イテテ、石を投げないで!)ダサイぃ。もうちょっと修正の余地があるのでちょこちょこ直していく予定。

Ruby の例外処理のやりかたにだんだん慣れてきた(?)気がする。システムコール/ライブラリコールそれぞれの返り値(返り血?)をベースにしたエラーチェックから解放されるプログラミングはやっぱりラク。^^ まだ「だいたいこの辺でエラー発生しがちだから囲っとくか」みたいなノリだけど…(汗。

書いていてる最中にうっかり ++ とかを使ってしまって「$ ri Numeric」とか「Ruby ++ なぜ 使えない」でググったりしたけど理由はわからずじまい。(count[:ok] + 1 の箇所)。twitter でボヤいていたら id:rubikitch さんから「そのへんの痒い所はすべてRuby逆引きハンドブックに書いてある! (続きはそこで(覇亜吐))」との返答をいただきました(←スミマセン。後半は心の声を読み取っただけです)。これはもう買うしかない!? ^^;

■要求

  • - コピー対象のファイルやディレクトリは入力ファイルから読み込むこと。ファイル中の「#」ではじまる行はコメント行として扱い、コピー対象から外すこと。
  • - コピー中にエラーが起こってもとりあえず処理は継続させること。
  • - コピー対象のファイル/ディレクトリそれぞれにつき正常(OK) or 異常(NG) 終了したかを出力させ、それぞれの数もカウントすること。(cron で回した時のログ確認がラクになるように)

■入力ファイル例(bacon_personal.cnf)

$ cat bacon_personal.cnf
hoge/etc/synergy.conf
/etc/dovecot/dovecot.conf
/etc/apt/sources.list
(...snip...)
/home/kyagi/.fetchmailrc
/home/kyagi/.procmailrc
/home/kyagi/.mail

■実行例

$ ./bacon.rb bacon_personal.cnf bacon_server_logs.cnf
NG ... hoge/etc/synergy.conf unknown file type: hoge/etc/synergy.conf
OK ... /etc/dovecot/dovecot.conf
OK ... /etc/init.d/rc.local
(...snip...)
OK ... /home/kyagi/.fetchmailrc
OK ... /home/kyagi/.procmailrc
OK ... /home/kyagi/.mail
ALL = 15, OK = 14, NG = 1
$

■実装(bacon.rb)

#!/usr/bin/env ruby

################################################################################
# reuquire/include
################################################################################
require "date"
require "fileutils"
include FileUtils

################################################################################
# configurable object
################################################################################
StoreDir = "/home/kyagi/cnf/backup"

################################################################################
# sub routine
################################################################################
def make_backup_dir ()
  dir = Date.today.to_s
  mkdir_p(dir) unless File.exist?(dir)
  return dir
end

def read_conf ()
  paths_valid = []
  ARGV.each do |config_file|
    File.readlines(config_file).each do |path|
      path.chomp!
      if (path =~ /^#/)
        next
      end
      paths_valid.push(path)
    end
  end
  return paths_valid
end

def do_copy (paths, dir)
  count = { :all => paths.count, :ok => 0, :ng => 0 }
  paths.each do |p|
    begin
      cp_r(p, dir, {:preserve => true})
    rescue => ex
      nil
    ensure
      unless (ex)
        puts "OK ... #{p}"
        count[:ok] = count[:ok] + 1
      else
        puts "NG ... #{p} #{ex.message}"
        count[:ng] = count[:ng] + 1
      end
    end
  end
  print "ALL = #{count[:all]}, OK = #{count[:ok]}, NG = #{count[:ng]}\n"
end

################################################################################
# main routine
################################################################################
begin
  paths = read_conf()
  Dir.chdir(StoreDir) do
    dir = make_backup_dir()
    do_copy(paths, dir)
  end
rescue => ex
  puts ex.message
end

Ruby逆引きハンドブック

Ruby逆引きハンドブック