【bat】パス長制限を回避できないときの最終手段

bat

バッチ(.bat)で深い階層や長い日本語名を扱うと、MAX_PATH(260文字)にぶつかり「指定されたパスが長すぎます」「The filename or extension is too long」などで処理が止まることがあります。通常は「長いパスを有効化」や \\?\ プレフィックス、作業ディレクトリの浅い場所への移動で解決しますが、それでも突破できない場面に向けて、実務で使える“最終手段”を整理します。

1. 可能なら SUBST で一時的に階層を短縮する

長いフルパスの上位ディレクトリを仮想ドライブに割り当て、残りを短縮して扱います。最も副作用が少なく、まず試す価値があります。

@echo off
rem 例)C:\very\very\long\path\to\project を X: に仮想割当
subst X: "C:\very\very\long\path\to\project" || (echo SUBST 失敗 & exit /b 1)

rem 以降は X:\src\... のように短いパスで操作
dir X:\src

rem 完了したら解除
subst X: /d

2. 拡張パス \?\/UNC 版 \?\UNC\server\share\... を徹底する

多くのコマンドは拡張長パスを解釈できます。ローカルは \\?\C:\...、ネットワークは \\?\UNC\server\share\... を使います。引用符も忘れずに。

@echo off
set "P=\\?\C:\very\long\日本語\とても\長い\パス\file.txt"
type "%P%" 2>nul
del "%P%"  2>nul

set "U=\\?\UNC\filesrv01\share\超\長い\パス\deep\file.log"
copy "%U%" "\\?\C:\work\file.log"

3. robocopy で「空ディレクトリをミラー」して深い木を削除する

Explorer や rmdir が諦める深いツリーでも、robocopy のミラー削除(/MIR)が突破できることがあります。空の一時フォルダを作って、対象へミラーリングします。

@echo off
set "TARGET=\\?\C:\very\long\deep\tree"
set "EMPTY=%TEMP%\empty_dir"
mkdir "%EMPTY%" 2>nul

robocopy "%EMPTY%" "%TARGET%" /MIR /R:1 /W:1 /NFL /NDL /NJH /NJS /NP
rmdir /s /q "%TARGET%"
rmdir "%EMPTY%" 2>nul

4. PowerShell 経由で .NET の API を使う(読書き・削除)

cmd の制約に阻まれるときは PowerShell に委譲します。-LiteralPath でワイルドカード展開を抑止し、長パスを確実に扱います。

@echo off
set "P=\\?\C:\very\long\日本語\階層\file.txt"
powershell -NoProfile -Command "Get-Content -LiteralPath '%P%' -Encoding UTF8 | Out-File -FilePath 'C:\work\out.txt' -Encoding UTF8"
powershell -NoProfile -Command "Remove-Item  -LiteralPath '%P%' -Force -Recurse -ErrorAction Stop"

5. 作業自体を「浅い場所」で行う(移動→処理→戻す)

ビルドや圧縮の前に、対象を一時的に浅いワークディレクトリへ集約してから処理します。コピーは robocopy、圧縮は tar(Win10以降)や 7-Zip を利用。

@echo off
set "SRC=\\?\C:\very\long\deep\project"
set "WRK=C:\wrk"

robocopy "%SRC%" "%WRK%\project" /E
rem ここで WRK 下を処理(短いパスで安定)
tar -cf "%WRK%\project.tar" -C "%WRK%" project

6. 8.3 形式(短縮名)を一時利用する

ボリュームで 8.3 名が有効なら、dir /x で短縮名を確認して操作できます。組織ポリシーによっては無効化されているため、恒久対応には不向きですが“最後の一押し”には有効です。

@echo off
dir /x "C:\very\long"
rem 例)VeryLo~1 のような短縮名を見つけて使う
copy "C:\VeryLo~1\DeepFo~1\file.txt" "C:\work\file.txt"

7. 削除に失敗する木を強制的に片付けるレシピ

長パス+使用中ファイルの合わせ技で詰まる場合の手順例です。プロセスを止め、長パスで再試行し、最後は robocopy /MIR と PowerShell の併用で締めます。

@echo off
setlocal
set "T=\\?\C:\very\long\locked\tree"

rem 1) 使っていそうなプロセスを停止(例)
taskkill /f /im App.exe 2>nul

rem 2) PowerShell で深い木を先に除去
powershell -NoProfile -Command "Remove-Item -LiteralPath '%T%' -Force -Recurse" 2>nul

rem 3) まだ残るなら空ミラーで一掃
set "EMPTY=%TEMP%\empty_dir"
mkdir "%EMPTY%" 2>nul
robocopy "%EMPTY%" "%T%" /MIR /R:1 /W:1 /NFL /NDL /NJH /NJS /NP 1>nul
rmdir /s /q "%T%"
rmdir "%EMPTY%" 2>nul
endlocal

8. それでも無理なときの見直しポイント

長いパスを恒常的に発生させない設計が本質解です。プロジェクトルートの浅い配置(例:C:\src)、生成物の出力先を短縮、アーカイブ名の簡素化、CI/CD でのワークディレクトリ固定(C:\a\w など)を徹底します。ネットワーク共有は UNC を基本とし、どうしても長い場合は SUBST で仮想ドライブ化してから操作します。

まとめ

まず SUBST で短縮し、ダメなら \\?\(UNC は \\?\UNC\...)を徹底。削除や同期は robocopy /MIR、読書き・削除は PowerShell の -LiteralPath に委譲。最終的に 8.3 短縮名も検討し、恒久対策としては「浅いワークディレクトリ運用」に改める――この順で進めれば、バッチでも“回避不能”に見えるパス長問題を実務的に突破できます。