robocopyとバッチファイルで世代管理バックアップをする方法

以前、「Windowsのバックアップはrobocopyが簡単便利で早い」で毎日のバックアップにはrobocopyが便利だよー、ということを書いたのですが、余談ばかり多くて結局ミラーリングバックアップの方法しか書いていませんでした。

ということで、今回はもう少し実用的なバックアップの世代管理について。

世代管理の必要性

OSがWindowsなら標準でrobocopyコマンドが使えますし、コマンドプロンプトで、

robocopy %USERPROFILE%\Documents d:\backup /MIR

とでも書けばマイドキュメントの中身をDドライブにサクっとバックアップ可能です。
(%USERPROFILE%は環境変数でユーザーフォルダのパスが自動的に入ります)

/MIRオプションを付けたミラーリングは初回こそ普通のファイルコピーですが、2回目以降は更新されたファイルのみコピーしますし、コピー元から削除されたファイルはコピー先でもちゃんと削除してくれる便利なコマンドです。

しかし、人間はミスをするイキモノですので、誤って編集または削除してしまったファイルがあることに、数日経ってから気がつくことも十分ありえます。………実際ぼく自身、そういう経験を過去に何度もしています。

ですので、本当に大事なデータは
D:\20190401\Documents
D:\20190402\Documents
D:\20190403\Documents
というようにフォルダに日付を付けてバックアップできたら便利だと思いませんか。
(2日前に消してしまったファイルがあるけど、3日前のバックアップから戻せばOK、みたいな)

これをバックアップの世代管理と言い、会社組織で使っているファイルサーバーでは昔から使われていた手法ですし、最近では個人やSOHO向けのNAS(Network Attached Storage)でも世代管理機能が付いているくらいです。

なので、てっとり早く実装するならNASをポチるのが簡単ですが、既にNASやファイルサーバーを持っている方や、簡易NASに付いている世代管理程度では足りず、もっと細かい操作をしたい場合には自分でバッチファイル等でスクリプトを組むのも良いでしょう。

※フォルダごとにミラーリングにしたり世代管理にしたり、曜日ごとに差分バックアップとフルバックアップを使い分けたり等

robocopyのコピー先に日付フォルダを指定する

バッチファイルには特殊な環境変数があるため、日付の指定はとても簡単。

具体的には %DATE% と書くだけで 2019/04/30 といった文字列が自動的に入ります。

但し、フォルダ名には / (スラッシュ)が付けられないため、この / だけ除去してあげる必要があるんですね。

その書き方がこちら。

echo %DATE:/=%

なんじゃこれって感じかもですが、: (コロン)で区切られた右側が置き換えの指定。「/」を「」(空文字)に変換してね、という指定なんです。

なので、今日が「2019/04/30」だとしたら、%DATE:/=%の中身は「20190430」となります。

では、実際にrobocopyで日付フォルダを指定してみましょう。
robocopy %USERPROFILE%\Documents D:\%DATE:/=% /MIR

はい、たったコレだけで、マイドキュメントの中身をDドライブの日付フォルダにバックアップできちゃいます。

%DATE%には常に今日の日付が入るため、Windowsのタスクスケジューラーで毎日決まった時間に実行するようにすればそれだけで、
D:\20190401\Documents
D:\20190402\Documents
D:\20190403\Documents
といった世代バックアップが出来上がるというわけです。

古いバックアップを削除する方法

ミラーリングの場合は元となるフォルダと同じくらいの容量のHDDがあれば空き容量についてはさほど気にする必要はありませんでしたが、世代バックアップとなるとそうはいきません。

2日で倍、3日で3倍、1ヶ月後には30倍になっているのですから。

仮に今、マイドキュメントの中身が100MBくらいしかなくても10日後にはバックアップHDD上に1GBの容量が必要なわけですね。いつか削除しないと容量が足りなくなります。

そこで、古いフォルダだけを削除するわけですが、これも意外と簡単です。バッチファイルの構文は慣れないと見づらいので、一瞬ウッとなるかもですが、こんな感じ。

set backupdir=D:\backup
set count=10
for /f "skip=%count%" %%a in ('dir "%backupdir%" /B /O-N') do (
rmdir /S /Q %backupdir%\%%a
)

解説

set backupdir=D:\backup の部分はバックアップ先の指定です。フォルダの削除を伴うため、サンプルとはいえD:\と書くのはちょっと怖いかなと思い、D:\backupとしました。

set count=10は世代管理数です。最新の10個のフォルダだけ残す、という意味ですね。

for 文は in の後に書かれたコマンドを実行し、その行数分、do 以降を繰り返し実行するという構文になります。

dir “%backupdir%” /B /O-N というコマンドを実行しているわけですが、これは D:\backup 下にあるフォルダを降順に並び替えてフォルダ名だけリストアップする、という動作をします。

つまり、
D:\backup\20190401\
D:\backup\20190402\
D:\backup\20190403\
というフォルダがあったとしたら、
20190403
20190402
20190401
という順序に文字列を返して %%a にセットするという動作になります。

そして skip=%count% というのは%count%で指定された回数分、処理を飛ばすという意味です。ここに10が指定された場合、dirで返されたフォルダ名を10個分飛ばすんですね。

日付の大きい順(新しい順)にフォルダ名を並び替え、最初の10個は無視して、残りについて do 以降の rmdir (フォルダ削除) を実行する、というわけです。

…なんだか、文章で説明するとややこしい気がしますね。

ともあれ、最初の2行はただの設定なので、実質3行だけで古い世代のフォルダ削除は出来る、ということです。

まぁ、正直バッチファイルの構文はわかりづらいので、VBScriptやPHPが得意なら、そっちで古いフォルダの削除処理をしてしまうのも手だと思います。
(実際ぼくはPHPでデイリーバックアップの処理を組んでいます)

まとめ

  • robocopyでの世代管理バックアップはバッチファイルで %DATE:/=% を使うだけで実現可能
  • 古い世代の削除もしたいなら、for /f “skip=10” といったスキップ機能を使うと簡単

といったところでしょうか。

世代管理はとても便利で、安心感が段違いですが、この作りだと毎回フルバックアップになるため、容量が大きいとバックアップHDDの容量がたくさん必要になるし、コピーする時間もそれ相当に長くなるのが難点といえば難点でしょうか。

差分バックアップをする方法もあるのですが、定期的にフルバックアップはしたほうが良いでしょうし、そうすると平日は差分バックアップ、土曜日だけフルバックアップ、とかそういう書き方になって複雑になるので………このへんの話はまたの機会に記事にしたいと思います。

『robocopyとバッチファイルで世代管理バックアップをする方法』へのコメント

  1. 名前:尾崎 投稿日:2021/06/29(火) 11:49:06 ID:ae11647b3 返信

    for /f “skip=%count%” %%a in (‘dir “%backupdir%” /B /O-N’) do (
    rmdir /S /Q %backupdir%\%%a
    の、dir “%backupdir%” /B /O-N ですが、backupディレクトリ内に何かのファイルが存在していた場合、それもカウントしてしまうため、/A:D を追加した方が安全ではないでしょうか。

    • 名前:とっちら 投稿日:2021/06/29(火) 14:47:40 ID:30a986cf1 返信

      たしかに!
      うちの環境ではディレクトリしか作らないため盲点でした