robocopy でサブフォルダ―『に』コピーしてみると

こんにちは システム開発一部の寺田です。

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 秒間待機しています...

次々と subtarget フォルダが作成され無限ループしてしまいます。
/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 バッチは普段の開発ではあまり出番はありませんが、
開発環境の構築や、定型的な自動処理などで役に立ちます。

既存のシステム開発の効率を向上したい。
新しいサービスを導入してみたい、そのようなメンバーを募集しております。

マネックスグループの採用にご興味のある方は、ぜひ以下募集をご覧ください。

www.monexgroup.jp