ネットで調べ物をしていると、次のページを見つけました。Rubyの配列を上手く使っていて、非常に勉強になる内容でした。
Ruby標準パッケージでディレクトリ比較を作ってrsyncモドキをする - それマグで!
http://takuya-1st.hatenablog.jp/entry/20100921/1285054620
せっかくなので、私も似たようなRubyスクリプトを作成してみました。
二つのディレクトリを比較し、追加や変更のあったファイル/ディレクトリを、別のディレクトリにコピーします。あるファイル/ディレクトリ一式を、他の人に提供する際に、2回目以降は、差分のファイル/ディレクトリのみを提供したい場合などに、使えるスクリプトです。(とは書いて見たものの、利用シーンは非常に限られていますね。)
使い方
次のように引数を3つ渡して、実行します。
ruby diffcopy.rb 変更前ディレクトリ 変更後ディレクトリ 出力ディレクトリ
たとえば、次のようなディレクトリに対して、スクリプト実行するとします。
変更前ディレクトリの中身
DiffFile.txt
Dir1OnlyFile.txt
SameFile.txt
変更後ディレクトリの中身
DiffFile.txt ※「変更前ディレクトリ」の同名ファイルと差異あり
Dir2OnlyFile.txt
SameFile.txt ※「変更前ディレクトリ」の同名ファイルと同じ
変更後ディレクトリにて追加、変更のあったファイルのみが選択されて、次のようなディレクトリが作成されます。この例では、ファイルのみを扱っていますが、サブディレクトリも同様に処理されます。
出力ディレクトリ
DiffFile.txt ※「変更後ディレクトリ」のファイル
Dir2OnlyFile.txt
スクリプト
require 'fileutils'
require 'pathname'
# ------------------------------------------------------------
# DiffCopyコマンドクラス
# ------------------------------------------------------------
class DiffCopy
def initialize(argv)
@args = Arguments.new(argv)
end
def run
@args.parse
if !@args.valid?
@args.print_usage
return
end
patharr1 = PathArray.new(@args.indir1)
patharr2 = PathArray.new(@args.indir2)
diffdata = DiffData.new(patharr1, patharr2)
filecopy = FileCopy.new(diffdata.update_path_array, @args.outdir)
filecopy.run
end
end
# ------------------------------------------------------------
# コマンドライン引数クラス
# ------------------------------------------------------------
class Arguments
attr_reader :indir1, :indir2, :outdir
def initialize(argv)
@argv = argv
end
def parse
@indir1 = @argv[0]
@indir2 = @argv[1]
@outdir = @argv[2]
end
def valid?
@indir1 && @indir2 && @outdir
end
def print_usage
puts "Usage: diffcopy 入力ディレクトリ1 入力ディレクトリ2 出力ディレクトリ"
end
end
# ------------------------------------------------------------
# パス配列クラス
# ------------------------------------------------------------
class PathArray < Array
attr_reader :dir
def initialize(dir, array = nil)
@dir = dir
if array == nil
# 与えられたディレクトリ配下の、ファイル/ディレクトリを再帰的に取得する。
Dir.chdir(dir) {
concat(Dir.glob("**/*"))
}
else
concat(array)
end
end
# 追加ファイル/フォルダを取得する。
def add_path_array(other)
PathArray.new(dir, self - other)
end
# 削除ファイル/フォルダを取得する。
def del_path_array(other)
PathArray.new(other.dir, other - self)
end
# 変更ファイルを取得する。
# ※フォルダの中身は別途確認するため、フォルダは、追加/削除のみ確認する。
def mod_path_array(other)
PathArray.new(dir, (self & other).delete_if {|e|
pathname = Pathname.new(File.expand_path(e, self.dir))
if pathname.directory?
true
elsif pathname.file?
FileUtils.cmp(File.expand_path(e, self.dir), File.expand_path(e, other.dir))
else
$stderr.puts("対象外パス:#{src}")
end
})
end
end
# ------------------------------------------------------------
# 差分クラス
# ------------------------------------------------------------
class DiffData
attr_reader :add_path_array, :del_path_array, :mod_path_array
def initialize(path_array1, path_array2)
@add_path_array = path_array2.add_path_array(path_array1)
@del_path_array = path_array2.del_path_array(path_array1)
@mod_path_array = path_array2.mod_path_array(path_array1)
end
def update_path_array
PathArray.new(@add_path_array.dir, (@add_path_array + @mod_path_array).sort)
end
end
# ------------------------------------------------------------
# ファイル/ディレクトリ差分コピークラス
# ------------------------------------------------------------
class FileCopy
def initialize(path_array, out_dir)
@path_array = path_array
@out_dir = out_dir
end
def run
@path_array.each {|e|
src = Pathname.new(File.expand_path(e, @path_array.dir))
dest = Pathname.new(File.expand_path(e, @out_dir))
if src.directory?
FileUtils.mkdir_p(dest, {:verbose => $VERBOSE})
elsif src.file?
FileUtils.mkdir_p(dest.parent, {:verbose => $VERBOSE})
FileUtils.cp(src, dest, {:verbose => $VERBOSE})
else
$stderr.puts("対象外パス:#{src}")
end
}
end
end
# ------------------------------------------------------------
# コマンド実行
# ------------------------------------------------------------
if __FILE__ == $0
main = DiffCopy.new(ARGV)
main.run
end
リポジトリ
上記のスクリプトは、次の場所で管理してあります。
https://github.com/kurukurupapa/TryRuby.git のdiffcopy.rb
動作確認環境
- Windows 8.1 64bit
- ruby 1.9.2p0 (2010-08-18 revision 29036) [x64-mswin64_80]