robocopy でミラーリングする前に結果確認を行う

<!-- vi: se tw=1000 noet sw=2 ts=4 sts=2 et wrap nolbr : -->

[前回](https://qiita.com/azuwai2/items/4f796081fe0d90122dbe)は、pythonで遊んでみた話でしたが、
今回は、windowsのバッチファイルを使ったすごくお手軽な記事です。

（使っているものは違いますが、話としては繋がっています。）

# 概要

お手軽に robocopy を使って、ミラーリングを行いたい。という話です。

バッチファイルから呼び出すので、バックアップ元と先を取り違えることはないのですが、（USBに刺す順番でやらかす可能性もありますが。）
本来は原本で名称変更や削除をしたかったのに、バックアップ先の方でやっちまった後で、ノールックミラーリングをブッパして、変更作業が台無しになる。
・・・みたいな醜態を二度と晒さないために、事前にどのようにミラーリングされるのかを確認してから、ミラーリングを実行するようにしよう。

という、ポカヨケ的な話です。

# 前回までのお話

なんでこんなものを作ろうと思ったのか、という割とどうでもいいストーリーです。

[前回の記事]((https://qiita.com/azuwai2/items/4f796081fe0d90122dbe))で作成したツール（以下、本記事では何の捻りもなく「写真整理ツール」と呼称）によって、過去の写真すべてを、一つのフォルダに集約できるようになりました。
整理されずに適当なフォルダへバックアップしてあったようなものも、写真整理を行っている原本フォルダへキレイに注ぎ足すことができました。

```
カメラ１─┬→ 写真一時置き場┬→ (写真整理ツール)→ 写真整理フォルダ
カメラ２─┤               │
：　　　─┤               │
スマホ　─┘               │
個別写真バックアップ×ｎ───┘
```

とてもスッキリです。
「ああ、この日にスマホでこんなの撮ってたんだー」とかいう発見があったりして、見返していて楽しくなります。

しかし、このツールで継ぎ足し先として指定できるのは、一か所だけです。
写真整理フォルダ自体をバックアップしてある方の外付けHDDは、グチャグチャだったりスカスカだったりなままです。

拙作写真整理ツールはファイルを移動してしまうので、もう一度バックアップ先に向けて、このツールを実行するというわけにもいきません。

簡単手軽にミラーリングしたいです。
具体的に言うと、写真整理ツールを実行して、写真整理フォルダ(原本)がある程度育つたびにミラーリングしたいです。

# 今までの私の写真整理
1. カメラから、それなりに整理しながら、写真整理フォルダ（原本）に取り込む。（簡単写真整理＆手作業とか、仕分けちゃん＆手作業とか）
1. 気が向いたときに、写真整理フォルダ（原本）を、外付けHDDにバックアップ。一応 robocopy でミラーリング（都度コマンド手打ち）
1. 大量の写真を取り込むときは、とても整理とかしてられないので、丸ごと引っこ抜いた状態のまま、外付けHDDに取り込んでおく。（もう一元管理できていない）

我ながら、整理というのもおこがましい。

# これからの私の写真整理
1. カメラやスマホから、少量でも大量でも、写真整理ツールを使って、写真整理フォルダ（原本）を育てる。
1. 写真整理フォルダが育ったら、外付けHDDへミラーリングを行う。

もはや、個別の丸ごとバックアップなど存在しません。
あるのは、「写真整理フォルダ（原本）」と「写真整理フォルダ（バックアップ）」の二つだけです。

# 全部まとまっていると、不要な写真も削除しやすい！

こうなってくると、写真整理フォルダを眺めて、要らないファイルをサクサク消したり、本当に必要な写真だけを眺めることができるということに気付きました。

「この時のやつ、もうちょっとよく撮れてるのがあれば、消しちゃいたいんだけど、どうだったかなぁ？」とかいうときに、全部の写真がそこに揃っているので、判断に迷わないのです。
ダメ写真をすぐ消せるので、そうやって写真をいくつか削除していると、断捨離スイッチが入るのか「マジでゴミ写真ばっかりだわ。なんでこんなもん残してるんだマジで。削除だ削除。」という気分になって容量削減にも大貢献します。

今までは、原本だとか言って、実のところいくつかある保存先の一つでしかないので、ちゃんと見返してなかったんですね。キレイにしようという気持ちが無かったんですねー。
一時的にキレイにしておきたいスイッチが入っているだけなのかもしれませんが。

で、要らねー写真はジャンジャン消すぜー！とやった後、ミラーリングしたら、消したはずの写真が復活していたのです。変えたはずのフォルダ名も戻っていたのです。
**バックアップが、原本と完全に同じなので、バックアップ先を眺めながら、作業をしていたことに気がつきませんでした。**

写真がロストしたというわけではないので、実質的なダメージはないのですが、悔しいです。
ノールックミラーリングは怖いです。



# 回避策
ミラーリング前に確認をおこない、問題がなければ実行する、という手順を踏みます。

robocopy には `/L`スイッチがあり、その説明は以下のようになっています。
```
リストのみ - いずれのファイルにも、コピー、タイムスタンプの追加、または削除を実施しません。
```

これでログを吐かせて、消したファイルを復活させようとしたりしてないかを、ちょっとでも確認できれば安心してミラーリングを実施できます。
しかし、毎回それを手動で実行するのはめんどくさいため、バッチファイルで自動化します。

# バッチファイルを作る
## リストの表示を英語にする

robocopy を使ったことがある人ならご存じだと思いますが、結果がとても見にくいです。
何タブで解釈しても、文字がズレズレなのです。
何個のファイルが何の動作の対象なのか、さっぱり読み解くことができません。

解決策はコンソールの文字コードを65001にすることです。65001が何なのかはググって。
終わったら932に戻しておきましょう。
```.bat
chcp 65001
robocopy ～
chcp 932
```

実際のバッチファイルの方では、標準出力にナンタラカンタラ申してくるのをnul送りしています。

## リストの確認を促す

`/L`で出力されたリストが、勝手に表示されてくるようにします。
lst という拡張子にしておいたので、好きなエディタにでも関連付けるといいでしょう。私はもちろん vim です。

開いてくるので、結果を確認します。
英語ですが、列位置がズレずに出力されるので、とても見やすく、確認しやすいです。

## 実行確認をする。

プロンプトに入力を促して、「MIRROR!」と入力された場合にだけ、ミラーリングを行うようにします。

```.bat
set /P input=">"
if "%input%"=="MIRROR!" (
  robocopy ～
)
```

これで、よっぽど朦朧とでもしていなければ、誤ってミラーリングを行ってしまうことはないでしょう。
二回目の robocopy からは`/L`を外して、代わりに`\J`を付与しています。
`/J`は
```
バッファーなし I/O を使用してコピーします (大きなファイルで推奨)。
```
というものです。デジカメの写真は個々のファイルとしては大きい扱いになるのかどうかわかりませんが、総量としては結構な量になることがあるので、とりあえず。

特筆するようなことはこれくらいです。


# 環境
OS
・Windows       10, 11


# バッチファイル

パスは直書きです。しかも、ドライブが違うだけで、パスは同じです。
こういうことをしてるから、原本を上塗りしたり、バックアップのほうを触っちゃったりするんだ、というお叱りはスルー。

最後の最後に気が付ければいいんです。むしろ途中でミスしてなくても、最後にミスしたらおしまいなんです。

ms932で保存してください。
もし「robocopy.bat」 などという名前で保存してしまうと、自分自身を呼び出すことになってしまって大変なことになるハズなので注意しましょう。

```roboco.bat
@echo off
cd %~dp0

rem 設定。
set src_dir="D:\写真整理"
set dst_dir="E:\写真整理"

set log_file=roboco.lst

rem 確認用ログ出力。
echo ミラーリング対象のリストアップ開始
mode CON CODEPAGE SELECT=65001 > nul
robocopy %src_dir% %dst_dir%  /MIR /L /UNILOG:%log_file% > nul
mode CON CODEPAGE SELECT=932 > nul
start %log_file%

rem 実行確認。
echo %log_file% を確認して、問題がなければ「MIRROR!」を入力してください。
echo （それ以外を入力すると、ミラーリングを行わずに終了します。）
set /P input=">"
if "%input%"=="MIRROR!" (
	echo ミラーリング開始
	mode CON CODEPAGE SELECT=65001 > nul
	robocopy %src_dir% %dst_dir%  /MIR /J /UNILOG:%log_file% > nul
	mode CON CODEPAGE SELECT=932 > nul
	echo ミラーリング終了
)
echo\
pause
```

## 2023/12/07 追記
コマンドラインで完結させましたが、外部ツールを使ってよければ、WinMerge とかで比較するのもありですね。
robocopy のリストファイルも見慣れると見やすいのですが。言い換えれば、慣れないと分かりづらいかもですし。

ということで、`robocopy /l`して、リストをstartしていたところを、WinMerge の起動に置き換えてみました。
WinMerge のパスは各自のインストール先に書き換えが必要です。WinMerge にはパス通してないでしょうし？

一応、`WinMergePortable.exe /r /e /m SizeDate`について軽く説明しておきますと、
`/r` は、サブフォルダまで対象に、
`/e` は、ESCキーで終了するように、
`/m SizeDate` は、比較方法を「更新日時とサイズ」
を指定しています。

```roboco2.bat
@echo off
cd %~dp0

rem 設定。
set src_dir="D:\日付別"
set dst_dir="E:\デジカメ(原本)\日付別"

start C:\PortableApps\WinMergePortable\WinMergePortable.exe /r /e /m SizeDate %src_dir% %dst_dir%

rem 実行確認。
echo WinMerge の結果を確認して、問題がなければ「MIRROR!」を入力してください。
echo （それ以外を入力すると、ミラーリングを行わずに終了します。）
set /P input=">"
if "%input%"=="MIRROR!" (
	echo ミラーリング開始
	mode CON CODEPAGE SELECT=65001 > nul
	robocopy %src_dir% %dst_dir%  /MIR /J /UNILOG:%log_file% > nul
	mode CON CODEPAGE SELECT=932 > nul
	echo ミラーリング終了
)
echo\
pause
```

start を使っているので、WinMerge を残したままコマンドライン側でミラーリング開始できますが、逆に、WinMearge が比較終了するどころか、起動すらしないうちから「MIRROR!」を入力すればそのまま進んでしまうのは、ちょっと嫌な感じです。

echoで「WinMeargeを確認したら終了してね！」とかメッセージを表示して、直接起動すれば WinMearge を完了させないと進めなくなりますが、たまに使う分にはそのほうが安心でしょうか。

まぁ、オマケということで。