こんにちは システム開発一部の寺田です。
Windows でフォルダのバックアップなど大量のファイルコピーを行うとき
robocopy ⧉
が便利です。
オプションを指定することでフォルダの同期やコピーの再試行ができます。
使用例
source
フォルダにファイルを作成し、サブフォルダごとコピー( robocopy /e
)
:prepare cd /d c:\ mkdir source source\sub echo test>source\test.txt echo test2>source\sub\test2.txt dir /s /b source :copy robocopy /e source target
target
フォルダが新たに作成され source
と同じ状態になる
c:\>dir /s /b source target c:\source\sub c:\source\test.txt c:\source\sub\test2.txt c:\target\sub c:\target\test.txt c:\target\sub\test2.txt
サブフォルダ『に』コピー
コピー先
に コピー元のサブフォルダ
を指定すると
注意 以下のコマンドは必ず /256
オプションを付けて実行してください。
robocopy /e /256 source source\sub\target
実行結果
c:\>robocopy /e /256 source source\sub\target 新しいディレクトリ 1 c:\source\ 100% 新しいファイル 6 test.txt 新しいディレクトリ 1 c:\source\sub\ 100% 新しいファイル 7 test2.txt 新しいディレクトリ 1 c:\source\sub\target\ 100% 新しいファイル 6 test.txt 新しいディレクトリ 1 c:\source\sub\target\sub\ 100% 新しいファイル 7 test2.txt 新しいディレクトリ 1 c:\source\sub\target\sub\target\ 100% 新しいファイル 6 test.txt 新しいディレクトリ 1 c:\source\sub\target\sub\target\sub\ 100% 新しいファイル 7 test2.txt 新しいディレクトリ 1 c:\source\sub\target\sub\target\sub\target\ 100% 新しいファイル 6 test.txt (中略) 新しいディレクトリ 1 c:\source\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\ 100% 新しいファイル 7 test2.txt 新しいディレクトリ 1 c:\source\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\ 2023/08/28 10:32:50 エラー 206 (0x000000CE) コピー先ディレクトリを作成しています c:\source\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\sub\target\ ファイル名または拡張子が長すぎます。 30 秒間待機しています...
次々と sub
と target
フォルダが作成され無限ループしてしまいます。
/256
オプションを付けないと、Windows または NTFS が許容する限界のパス長までコピーが行われます。
考察
robocopy の内部処理において、
あらかじめコピーするファイルリストを作成するのではなく、
コピー元のフォルダから再帰的に順次コピーを行っていると推察されます。
単純にサブフォルダを対象したときだけではなく、
恣意的な例になりますが、マウントした別ドライブなどをコピー先にしたときでも発生します。
:mount subst s: c:\source\sub robocopy /e /256 source s:\target :unmount subst /d s:
対処方法1 - パスの比較
コピー先がサブフォルダかどうかの判定はパスでの比較が容易です。
ただし、コピー元やコピー先には複数のパス形式が入力できるため、
相対パスや絶対パス、パスの区切り文字( \
or /
)の統一に pushd
を使います。
:input set from="c:\source" set to="source/sub/target" :normalize pushd %from% set from="%cd%" popd pushd %to% set to="%cd%" popd echo %from% %to% :compare echo %to% | find %from% > nul if %errorlevel%==0 ( echo %to% は %from% のサブフォルダです。 ) else ( echo %to% は %from% のサブフォルダではありません。 )
実行結果
"c:\source\sub\target" は "c:\source" のサブフォルダです。
対処方法2 - File ID を取得
ファイルやフォルダが厳密に一致しているかどうかは File ID で判断できます。
fsutil file ⧉
で File ID の取得が可能です。
直接サブフォルダであるか判定することはできないため、
コピー元のサブフォルダを再帰的に巡回し、コピー先と File ID が一致しないことを確認します。
※ setlocal
を使っているため bat ファイルからの実行が必要です。
@echo off :prepare setlocal enabledelayedexpansion subst s: c:\source\sub :input set from="c:\source" set to="s:\target" for /f "DELIMS=" %%A in ('fsutil file queryfileid %to%') do set fileid=%%A echo id:[%fileid:~10,34%] path:%to% for /d /r %from% %%d in (*) do ( for /f "DELIMS=" %%B in ('fsutil file queryfileid "%%d"') do set fileid2=%%B echo id:[!fileid2:~10,34!] path:"%%d" :compare if !fileid! == !fileid2! ( echo %to% は %from% のサブフォルダ[%%d]です。 goto :end ) ) echo %to% は %from% のサブフォルダではありません。 :end subst /d s: endlocal pause
実行結果
id:[0x0000000000000000013300000005c89e] path:"s:\target" id:[0x00000000000000000160000000012d61] path:"c:\source\sub" id:[0x0000000000000000013300000005c89e] path:"c:\source\sub\target" "s:\target" は "c:\source" のサブフォルダ[c:\source\sub\target]です。
異なるドライブレターのファイルについても同一の File ID であることが確認ができます。
まとめ
robocopy のコピー先に特殊なパスを入れたときのご紹介でした。
パスのバリエーションが多く、正しいチェックは難しいのですが、
いくつかの試行錯誤したチェック方法を検討しました。
Windows バッチは普段の開発ではあまり出番はありませんが、
開発環境の構築や、定型的な自動処理などで役に立ちます。
既存のシステム開発の効率を向上したい。
新しいサービスを導入してみたい、そのようなメンバーを募集しております。
マネックスグループの採用にご興味のある方は、ぜひ以下募集をご覧ください。