任意ディレクトリをRobocopyの速さを生かして世代管理するバッチを書いた

Robocopyって同期出来るから速いじゃん?
バックアップなんだから世代管理したいじゃん?
あれ、矛盾してね?

前置き

以前書いたRobocopyの記事。
Robocopyのオプションについてはこちらを参照下さい。
nyanshiba.hatenablog.com

Robocopy/MIRで同期のようなバックアップが出来るため、コピー先が同じディレクトリであれば2回目以降はとても早くバックアップできる。
それを利用しつつ、世代管理もしようってのがこのバッチ。
指定数世代が溜まったら、古いフォルダからリネームされ、そこへ高速でバックアップされるようになる。

バッチ

@echo off

rem --------------------環境変数設定--------------------
rem コピー先のフォルダを残す数を指定
set dst_num=5
rem コピー先ディレクトリを指定
set "dst=G:"

rem --------------------フォルダ名の日付時刻--------------------
rem yyMMdd_Hmm
rem 自分用(17/08/30 (水) 17:35->170830_1735)
set mydate=%date:~0,2%%date:~3,2%%date:~6,2%_%time:~0,2%%time:~3,2%
rem 初期設定(2017/08/30 17:35 -> 170830_1735)
rem set mydate=%date:~2,2%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%
rem スペースを0に置き換える
set mydate=%mydate: =0%

rem --------------------フォルダ作成--------------------
rem dirでコピー先のフォルダをフォルダ名のみ日付順で表示し、新しい方からdst_num個飛ばし、該当フォルダがあれば削除する
for /f "skip=%dst_num%" %%a in ('dir "%dst%" /ad-s /b /o-n') do (
    echo 削除:"%dst%\%%a"
    rd /s /q "%dst%\%%a"
)
rem dirでコピー先のフォルダをフォルダ名のみ日付順で表示し、新しい方からdst_num-1個飛ばし、該当フォルダがあればリネーム
set /a dst_num1=dst_num-1
for /f "skip=%dst_num1%" %%a in ('dir "%dst%" /ad-s /b /o-n') do (
    ren "%dst%\%%a" "%mydate%"
    echo コピー先:%dst%\%mydate%^(差分:%dst%\%%a^)
)
rem コピー先に名前が変数mydateなフォルダが無ければ作成
if not exist "%dst%\%mydate%" (
    md "%dst%\%mydate%"
    echo コピー先:%dst%\%mydate%^(新規^)
)

rem --------------------ログ作成--------------------
rem コピー先に概要ログを作成
echo %mydate% >> "%dst%\robocopy_summary.txt"

rem --------------------robocopy--------------------
rem コピー元ディレクトリを指定
set "src=C:\hoge"
rem コピー先サブディレクトリを指定
set "dst_sub=\hoge"
rem robocopyサブルーチンを実行
call :copysub

echo 処理終了
pause
exit

:copysub
rem --------------------robocopyサブルーチン--------------------
echo %src%^-^>%dst%\%mydate%%dst_sub%
rem 詳細ログがコピー先のフォルダ内に出力される
robocopy "%src%" "%dst%\%mydate%%dst_sub%" /MIR /XO /NP /XA:SH /R:3 /W:5 /LOG+:"%dst%\%mydate%\robocopy_details.txt" /XD "C:\DTV\backupandsync" "C:\DTV\ts" /FFT
exit /b

解説

コピー先のディレクトリ構造

dst
 ┣mydate
 ┃  ┣dst_sub
 ┃  ┣dst_sub
 ┃  ┣dst_sub
 ┃  ┗robocopy_details.txt
 ┣mydate
 ┃  ┣dst_sub
 ┃  ┣dst_sub
 ┃  ┣dst_sub
 ┃  ┗robocopy_details.txt
 ┣mydate
 ┃  ┣dst_sub
 ┃  ┣dst_sub
 ┃  ┣dst_sub
 ┃  ┗robocopy_details.txt
 ┗robocopy_summary.txt

コピー先を決める仕組み

1.コピー先のフォルダ数が一定より大きい:古い方から該当フォルダを削除
フォルダを残す数dst_numを小さい値に変更した際、2.が実行されてしまうのを防ぐ役割も担っている。

rem dirでコピー先のフォルダをフォルダ名のみ日付順で表示し、新しい方からdst_num個飛ばし、該当フォルダがあれば削除する
for /f "skip=%dst_num%" %%a in ('dir "%dst%" /ad-s /b /o-n') do (
    echo 削除:"%dst%\%%a"
    rd /s /q "%dst%\%%a"
)

例:フォルダを5個残す場合、コピー先にフォルダが7個あれば、古い方から2個削除し5個に

dst
 ┣180204 <-削除
 ┣180211 <-削除
 ┣180218
 ┣180225
 ┣180304
 ┣180311
 ┗180318
↓
dst
 ┣180218
 ┣180225
 ┣180304
 ┣180311
 ┗180318

2.コピー先のフォルダ数が一定以上:最も古いフォルダを現在の日付にリネームして、そこにバックアップ

rem dirでコピー先のフォルダをフォルダ名のみ日付順で表示し、新しい方からdst_num-1個飛ばし、該当フォルダがあればリネーム
set /a dst_num1=dst_num-1
for /f "skip=%dst_num1%" %%a in ('dir "%dst%" /ad-s /b /o-n') do (
    ren "%dst%\%%a" "%mydate%"
    echo コピー先:%dst%\%mydate%^(差分:%dst%\%%a^)
)

例:フォルダを5個残す場合、コピー先にフォルダが5個あれば、最も古いフォルダを現在の日付にリネームし、そこに差分コピー

dst
 ┣180218 <-リネーム
 ┣180225
 ┣180304
 ┣180311
 ┗180318
↓
dst
 ┣180325
 ┣180225
 ┣180304
 ┣180311
 ┗180318

3.コピー先のフォルダ数が一定未満:現在の日付のフォルダにバックアップ

rem コピー先に名前が変数mydateなフォルダが無ければ作成
if not exist "%dst%\%mydate%" (
    md "%dst%\%mydate%"
    echo コピー先:%dst%\%mydate%^(新規^)
)

例:フォルダを5個残す場合、コピー先にフォルダが5個以上無ければ、現在の日付のフォルダがされ、そこにコピー

dst
 ┣180304
 ┣180311
 ┗180318
↓
dst
 ┣180304
 ┣180311
 ┣180318
 ┗180325 <-作成

使い方

1.バッチをテキストエディタにコピペし、拡張子.bat、文字コードShift-JIS(ANSI)で保存。
※ファイル名はrobocopy以外
2.以下を自分の環境や目的に合わせて変更する。

rem --------------------環境変数設定--------------------
rem コピー先のフォルダを一定数残す数を指定
set dst_num=5
rem コピー先ディレクトリを指定
set "dst=G:"

dst_num世代管理する際、何世代残すかを指定する。
大きなファイルを扱う場合には、ストレージの容量を超えないように調整すること。
dstコピー先の元ディレクトリを指定。
スペースや記号等が入るディレクトリでは、"set dst=G:"のように囲うこと。

rem --------------------robocopy--------------------
rem コピー元ディレクトリを指定
set "src=C:\hoge"
rem コピー先サブディレクトリを指定
set "dst_sub=\hoge"
rem robocopyサブルーチンを実行
call :copysub

srcコピー元ディレクトリを指定
dst_subコピー先サブディレクトリを指定
src=C:\hogedst=G:mydate=180322_2249dst_sub=\hogeの場合、コピー元はC:\hoge、コピー先はG:\180322_2249\hogeとなる。
set dst_sub=でコピー先はG:\180322_2249となる。
call :copysub上記内容でRobocopyを呼び出す

rem --------------------robocopy--------------------
set "src=C:\hoge"
set "dst_sub=\hoge"
call :copysub
set "src=C:\bin\foo"
set "dst_sub=\foo"
call :copysub
set "src=D:\fuga"
set "dst_sub=\fuga"
call :copysub

とすると、このように任意のディレクトリに対してそれぞれバックアップすることができる。
コピー元C:\hoge、コピー先G:\180322_2249\hoge
コピー元C:\bin\foo、コピー先G:\180322_2249\foo
コピー元D:\fuga、コピー先G:\180322_2249\fuga

:copysub
rem --------------------robocopyサブルーチン--------------------
echo %src%^-^>%dst%\%mydate%%dst_sub%
rem 詳細ログがコピー先のフォルダ内に出力される
robocopy "%src%" "%dst%\%mydate%%dst_sub%" /MIR /XO /NP /XA:SH /R:3 /W:5 /LOG+:"%dst%\%mydate%\robocopy_details.txt" /XD "C:\DTV\backupandsync" "C:\DTV\ts" /FFT
exit /b

robocopy "%src%" "%dst%\%mydate%%dst_sub%"の後にオプションを書く。お好みに。
/LOG+:"%dst%\%mydate%\robocopy_details.txtで詳細ログがコピー先のフォルダ内に出力される。

3.実行
・バッチファイルをダブルクリック
・タスクスケジューラに登録する
etc.