再帰的なファイルのバックアップ(Ruby)

次の動作をします。コピー先のディレクトリに

  • ファイルがない(か変更されている)場合、ファイルを(上書き)コピーします。
  • ディレクトリがない場合、再帰的にコピーします。
  • ディレクトリがある場合、それが変更されていればそのディレクトリに入って、もう一度(再帰的に)最初から同じことをします。
  • それ以外の場合は、何もしません。

配列 dir の中に [["コピー元のディレクトリの名前", "コピー先のディレクトリの名前"], [..., ...], ...] を書き込んでおきます。なお、配列に入れたコピー先のディレクトリが、予め存在していることが必要です。

基本的に Linux用です。Windows ではファイルの変更日時などの考え方が Linux とは異なるので、期待したような動作をしません。

require 'fileutils'

def fcopy(snd, rsv)
  Dir.chdir(rsv)
  r_fn = Dir.glob("*")
  Dir.chdir(snd)

  Dir.glob("*").each do |fname|
    s = File.join(snd, fname)
    r = File.join(rsv, fname)
    mts = File.stat(s).mtime
    mtr = nil
    mtr = File.stat(r).mtime if r_fn.include?(fname)
    
    if FileTest.directory?(s)
      if r_fn.include?(fname)
	fcopy(s, r) if mts > mtr and FileTest.directory?(r)
      else
        puts "folder copy: #{fname}"
        FileUtils.cp_r(s, r)
      end
    elsif !r_fn.include?(fname) or mts > mtr
      puts "file copy: #{fname}"
      FileUtils.cp(s, r)
    end
  end
end

t = Time.now

dir = [['/home/***/Pictures', '/media/***/Transcend/Linux Mint backup/Pictures'],
       ['/home/***/Documents', '/media/***/Transcend/Linux Mint backup/Documents']]
dir.each do |d|
  next if File.stat(d[0]).mtime == File.stat(d[1]).mtime
  fcopy(d[0], d[1])
end

printf("\nRuntime: %.3f [sec]\n", Time.now - t)

なお、コピー先のディレクトリやファイルは、コピー元にないものでも一切削除されません。コピー元を削除してもコピー先のファイル乃至ディレクトリは溜まっていく一方なので、要らないものは手で削除して下さい。


※追記
コピー元にファイルやディレクトリがなくなった場合に、コピー先も削除するバージョンを書きました。