rubyの標準パッケージの状態でディレクトリ内のファイルの比較を考えた。
rsync的にディレクトリ間のファイルの有無を調べたいと思います。
SRCディレクトリとDESTディレクトリのファイル名一覧を取出し、
それ使ってSRCとDESTのファイルの違いを探したいと思いました。
二つのディレクトリを比較する
require 'fileutils' require 'kconv' src = 'C:/Users/takuya/Desktop/src/' dest = 'C:/Users/takuya/Desktop/dest/' #ファイル名一覧を作る Dir.chdir src src_files = Dir.glob "./**/*" Dir.chdir dest dest_files = Dir.glob "./**/*" ######################### #src にあってDestにないもの ######################### puts "#src にあってDestにないもの" puts (src_files - dest_files) ######################### #dest にあってsrcにないもの ######################### puts "#dest にあってsrcにないもの" puts (dest_files -src_files) ##################################### #両方のディレクトリにあるファイル ##################################### #二つともにあるもの puts "#二つともにあるもの" puts (dest_files & src_files) #二つにあるモノのウチ中身が違うもの puts "#二つにあるモノのウチ中身が違うもの" puts (dest_files & src_files).reject{|e| FileUtils.cmp File.expand_path(e,src) , File.expand_path(e,dest) } #二つにあるモノのウチ中身が違う一覧中で、destの方が古いもの puts "#二つにあるモノのウチ中身が違う一覧中で、destの方が古いもの" puts (dest_files & src_files).reject{|e| FileUtils.cmp( File.expand_path(e,src) , File.expand_path(e,dest) )}.select{|e| (File.mtime(File.expand_path(e,src)) > File.mtime( File.expand_path(e,dest))) } #両方にあるファイル名で中身が違うもので src の方が古いもの puts "#両方にあるファイル名で中身が違うもので src の方が古いもの" puts (dest_files & src_files).reject{|e| FileUtils.cmp( File.expand_path(e,src) , File.expand_path(e,dest) )}.select{|e| (File.mtime(File.expand_path(e,src)) < File.mtime( File.expand_path(e,dest))) } #同じファイルが、違う名前で重複していたらTrueを返す require 'digest/md5' require "pp" puts "#同じファイルが、違う名前で重複していたらTrueを返す" a Dir.glob("C:/Users/takuya/Pictures/100MEDIA/**/*").map{|e| File.expand_path(e,dest) }.inject({}){|a,e| key = Digest::MD5.hexdigest(File.open(e ,"rb").read); a[key] = a.fetch(key,[]).push e a }.select{|k,v| true if v.size > 1 } p Hash[a].size > 0
比較した一覧のファイルをコピーする
このファイル一覧を使って、いろいろなコピーが出来そうですよね。バックアップや同期に使えるかも。
SRC → DESTの一方向同期ならすぐ出来るんじゃないか
rsync -av --delete っぽいこと
require 'fileutils' src = 'C:/Users/takuya/Desktop/src/' dest = 'C:/Users/takuya/Desktop/dest/' Dir.chdir src src_files = Dir.glob "./**/*" Dir.chdir dest dest_files = Dir.glob "./**/*" #先に作った一覧をつかってコピーすればいい files =[] ## src → dest へ一方向同期の場合 #両方にあるファイルのうち、中身が違っていて、dest側が古いファイル一覧 files = (dest_files & src_files).reject{|e| FileUtils.cmp( File.expand_path(e,src) , File.expand_path(e,dest) )}.select{|e| (File.mtime(File.expand_path(e,src)) > File.mtime( File.expand_path(e,dest))) } #srcにあって destに無いもの files = files + (src_files - dest_files) #srcファイルをdestに上書き files.each{ |e| FileUtils.copy( File.expand_path(e,src) , File.expand_path(e,dest) ) } #rsyncの --delete と同様のことをするには (dest_files - src_files).each{ |e| FileUtils.remove( File.expand_path(e,dest) ) }
これでrsyncっぽいコトがrubyで出来るようになるね。配列の扱いが賢いのでruby 書きやすい。
windows 版 rsync こと cwRsync や cygwin Rsync はファイルのオーナーシップやセキュリティー設定が書き換わるのでなんか好きになれない。どうやって回避して良いか分からなかったので、Rubyでやりました。
追記
ファイルの中身を比較するととっても遅いです。2MBのデジカメ写真の比較をした場合
1000ファイルで計測
更新日時だけをチェックした場合
user system total real 0.031000 0.453000 0.484000 ( 1.242000)
ファイルの中身を比較した場合
user system total real 20.920000 13.150000 34.070000 (178.394000)
驚きの差です。インデックスが使える更新日時と直接ファイルを開いてロードするのとではやっぱり、大きく違いますね。
2011-07-06 追記
Windows のRuby は mtime がミリ秒を返さないので,ファイルの時刻比較がうまくいかないことがある。mtime はWindowsの制約なのでなんとも・・・・
ruby の mtimeで解決を図るにはWin32APIの力を借りるしかなさそう
http://d.hatena.ne.jp/electrolysis/20070726/1185409408