3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

二つのディレクトリの差分を別ディレクトリにコピーするスクリプトを作ってみました

Posted at

ネットで調べ物をしていると、次のページを見つけました。Rubyの配列を上手く使っていて、非常に勉強になる内容でした。

Ruby標準パッケージでディレクトリ比較を作ってrsyncモドキをする - それマグで!
http://takuya-1st.hatenablog.jp/entry/20100921/1285054620

せっかくなので、私も似たようなRubyスクリプトを作成してみました。

二つのディレクトリを比較し、追加や変更のあったファイル/ディレクトリを、別のディレクトリにコピーします。あるファイル/ディレクトリ一式を、他の人に提供する際に、2回目以降は、差分のファイル/ディレクトリのみを提供したい場合などに、使えるスクリプトです。(とは書いて見たものの、利用シーンは非常に限られていますね。)

使い方

次のように引数を3つ渡して、実行します。

diffcopy.bat
ruby diffcopy.rb 変更前ディレクトリ 変更後ディレクトリ 出力ディレクトリ

たとえば、次のようなディレクトリに対して、スクリプト実行するとします。

変更前ディレクトリの中身

DiffFile.txt
Dir1OnlyFile.txt
SameFile.txt

変更後ディレクトリの中身

DiffFile.txt ※「変更前ディレクトリ」の同名ファイルと差異あり
Dir2OnlyFile.txt
SameFile.txt ※「変更前ディレクトリ」の同名ファイルと同じ

変更後ディレクトリにて追加、変更のあったファイルのみが選択されて、次のようなディレクトリが作成されます。この例では、ファイルのみを扱っていますが、サブディレクトリも同様に処理されます。

出力ディレクトリ

DiffFile.txt ※「変更後ディレクトリ」のファイル
Dir2OnlyFile.txt

スクリプト

diffcopy.rb
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]

参考ページ

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?