【bat】パス長制限(MAX_PATH)を通常手段で回避できないときの最終手段集|SUBST・robocopy・PowerShell・WSL・ジャンクション・8.3短縮名まで徹底解説

bat

Windowsのパス長制限(MAX_PATH=260文字)は、レジストリで「長いパスを有効化」したり\\?\ プレフィックスを使えば大半は解決できます。しかし「レジストリ変更が組織ポリシーで禁止されている」「対象コマンドが \\?\ に非対応」「ロックと長パスが重なって手が出ない」といった状況では、標準手段だけでは突破できません。本記事では、そうした「それでもダメなとき」に使える実践的な最終手段を体系的に解説します。

標準的なパス長制限の回避方法(レジストリ設定・\\?\ プレフィックス・subst・robocopy の基本)についてはWindowsのパス長制限(MAX_PATH=260)完全攻略を参照してください。本記事はその先のケースを扱います。
スポンサーリンク

コマンド別の長パス対応状況

最終手段を選ぶ前に、使おうとしているコマンドが長パスに対応しているかを確認します。

コマンド \\?\ 対応 260文字超え 備考
robocopy 長パスの筆頭手段。/256 フラグ(Win10以降)
xcopy 環境によって動作が不安定
copy × × 260文字制限を超えると失敗
del \\?\ 付きなら長パス可
rd /s /q × 深いツリーで失敗しやすい
dir \\?\ 付きで列挙可能
move × × 長パスは基本的に失敗
powershell (Remove-Item等) -LiteralPath と \\?\ で対応
tar(Win10以降内蔵) 長パスを含むアーカイブに対応

最終手段①:SUBST で仮想ドライブに割り当てる

長いパスの途中までを仮想ドライブに割り当て、残りのパスを短くします。ポリシー変更不要で副作用が少なく、まず試す価値があります。

SUBST で仮想ドライブ化
@echo off
rem C:\very\long\path\to\project を X: に割り当て
subst X: "C:\very\long\path\to\project"
if errorlevel 1 (
    echo SUBST 失敗: ドライブ文字が使用中の可能性があります
    exit /b 1
)

rem 以降は X:\ 以下の短いパスで操作
dir X:\src
robocopy "X:\src" "D:\backup\src" /E

rem 処理後は必ず解除
subst X: /d
SUBST の注意点:セッションをまたいで有効にしたい場合は subst ではなくmklink /D(シンボリックリンク)か mklink /J(ジャンクション)を使います。SUBST は再起動すると消えます。また昇格プロセスとの共有には制限があります。
未使用ドライブレターを自動検出して SUBST
@echo off
setlocal EnableDelayedExpansion
set "SRC=C:\very\long\path\to\project"

rem 未使用のドライブレターを探す(Z:から順番に)
set "DRV="
for %%L in (Z Y X W V U T S) do (
    if "!DRV!"=="" (
        subst %%L: "!SRC!" >nul 2>&1
        if not errorlevel 1 set "DRV=%%L"
    )
)
if "!DRV!"=="" (
    echo 使用可能なドライブが見つかりません
    exit /b 1
)

echo !DRV!: を使用中
rem === ここで !DRV!:\ を使って処理 ===
dir "!DRV!:\"

subst "!DRV!:" /d
endlocal

最終手段②:mklink でジャンクション・シンボリックリンクを使う

SUBST は再起動すると消えますが、mklink /J(ジャンクション)は永続します。深い階層の途中に短い名前のジャンクションを作り、有効なパス長を短縮します。

mklink /J でジャンクションを作成
@echo off
net session >nul 2>&1 || goto :no_admin

rem 深い階層の途中に短いジャンクションを作る
rem C:\proj\deep\nested\module を C:\m にリンク
mklink /J "C:\m" "C:\proj\deep\nested\module"

rem 以降は C:\m\src\... という短いパスで操作
dir "C:\m\src"

rem 作業後はリンクを削除(/J はリンクだけ消え、実体は残る)
rmdir "C:\m"
goto :eof

:no_admin
echo 管理者権限が必要です(mklink /J はディレクトリジャンクション作成に必要)
exit /b 1
種類 コマンド 対象 管理者権限 再起動後
ジャンクション mklink /J ローカルディレクトリ 必要 維持
シンボリックリンク(ディレクトリ) mklink /D ローカル・ネットワーク 必要 維持
シンボリックリンク(ファイル) mklink ファイル 必要 維持
仮想ドライブ subst ローカルディレクトリ 不要 消える

最終手段③:robocopy /MIR で深いツリーを削除する

rd /s /q が深いツリーで失敗するとき、空フォルダを用意して robocopy /MIR(ミラーリング)すると対象フォルダが空になります。その後に rd で削除します。

robocopy /MIR で深いツリーを削除
@echo off
setlocal
set "TARGET=C:\very\long\deep\tree"
set "EMPTY=%TEMP%\empty_%RANDOM%"
mkdir "%EMPTY%"

rem 空フォルダを対象にミラーリング → 中身がすべて消える
robocopy "%EMPTY%" "%TARGET%" /MIR /R:1 /W:1 /NFL /NDL /NJH /NJS /NP >nul

rem 空になったフォルダを削除
rd /s /q "%TARGET%" 2>nul
rd "%EMPTY%" 2>nul

if exist "%TARGET%" (
    echo 削除失敗: まだ残っています
    exit /b 1
)
echo 削除完了
endlocal
exit /b 0

robocopy/256 フラグ(Windows 10 1607以降)で MAX_PATH を超えるパスも扱えます。

robocopy /256 で長パスのコピー・移動
@echo off
rem /256: 長いパスを有効にして処理
robocopy "C:\very\long\path\source" "D:\backup\dest" /E /256 /R:1 /W:1 /NFL /NP

最終手段④:PowerShell -LiteralPath で操作する

cmd の制限を受けずに PowerShell の .NET API を使うことで、長パスのファイル操作が確実になります。-LiteralPath を使うとワイルドカードや特殊文字の展開を抑止できます。

PowerShell で長パスの読み書き・削除
@echo off
set "P=\\?\C:\very\long\日本語\path\file.txt"

rem 読み込み
powershell -NoProfile -Command ^
    "Get-Content -LiteralPath '%P%' -Encoding UTF8 | Out-File -FilePath 'C:\work\out.txt' -Encoding UTF8"

rem 削除(再帰的)
powershell -NoProfile -Command ^
    "Remove-Item -LiteralPath '%P%' -Force -Recurse"

rem コピー
powershell -NoProfile -Command ^
    "Copy-Item -LiteralPath '%P%' -Destination 'C:\work\file.txt' -Force"
PowerShell で長パスのファイル一覧を取得
@echo off
rem 260文字を超えるパスを列挙
powershell -NoProfile -Command ^
    "Get-ChildItem -LiteralPath 'C:\deep\path' -Recurse |" ^
    "Where-Object { $_.FullName.Length -gt 260 } |" ^
    "Select-Object FullName"

最終手段⑤:8.3形式の短縮名を使う

NTFSボリュームで 8.3 形式の短縮名が有効になっていれば、長いフォルダ名を短縮名で置き換えることでパス長を削減できます。組織ポリシーで無効化されている場合は使えないため、事前に確認が必要です。

8.3短縮名の確認と活用
@echo off
rem dir /x で短縮名を確認
dir /x "C:\very"

rem 例)LongFo~1 のような短縮名が表示される
rem 短縮名を使って操作(全角文字の長いフォルダ名を回避)
copy "C:\VeryLo~1\SubDir~1\file.txt" "C:\work\file.txt"

rem 有効かどうかの確認
fsutil 8dot3name query C:\
短縮名が無効(fsutil で 8dot3name creation is disabled と表示)な場合は、管理者権限で fsutil 8dot3name set C: 0 を実行すると有効化できます。ただし大量ファイルがある場合は fsutil での有効化後に fsutil 8dot3name scan C:\ /v で既存ファイルの短縮名を生成する必要があります。

最終手段⑥:WSL(Windows Subsystem for Linux)を使う

WSL が使える環境では Linux のファイル操作コマンドでWindowsのパス制限を回避できます。特にシンボリックリンクの多いプロジェクトや、深いネスト構造の一括削除に有効です。

WSL で長パスのファイルを操作
@echo off
rem Windowsのパスを /mnt/c/ 形式に変換してWSLで操作
wsl rm -rf "/mnt/c/very/long/deep/nested/folder"

rem WSL で長パスのファイルをコピー
wsl cp "/mnt/c/very/long/path/file.txt" "/mnt/c/work/file.txt"

rem WSL で 260文字超えのファイルを一覧
wsl find "/mnt/c/deep/path" -name "*.log" -type f
注意:WSL から Windows ファイルシステムを操作する際、パーミッションや改行コード(CRLF/LF)の変換に注意が必要です。バイナリファイルは wsl cp でコピーすると破損する可能性があります。テキスト処理は問題ありませんが、実行ファイルや設定ファイルの扱いは慎重に行ってください。

最終手段⑦:浅い一時ディレクトリで処理してから戻す

深い階層での直接作業をあきらめ、浅い一時ディレクトリに移してから処理する方法です。ビルド・圧縮・変換など、入出力のパス長制限がある処理で特に有効です。

浅い一時ディレクトリで処理するパターン
@echo off
setlocal
set "SRC=C:\very\long\deep\project\src"
set "WRK=C:\wrk\tmp_%RANDOM%"

rem 作業ディレクトリを作成
mkdir "%WRK%" 2>nul

rem 処理対象を浅い場所にコピー
robocopy "%SRC%" "%WRK%\src" /E /256 /R:1 /W:1 /NFL /NP >nul

rem 短いパスで処理(例:ビルドや圧縮)
pushd "%WRK%"
tar -cf "C:\output\result.tar" src
popd

rem 後片付け
robocopy "%TEMP%\empty_ref" "%WRK%" /MIR /R:1 /W:1 /NP >nul 2>&1
rd /s /q "%WRK%" 2>nul

endlocal
echo 完了: C:\output\result.tar

ロックと長パスが重なった場合の複合対処

プロセスがロック中かつ長パスで削除できない、という最も厄介なケースへの対処手順です。

ロック + 長パスの複合問題への対処
@echo off
setlocal
set "T=\\?\C:\very\long\locked\folder"

rem 1) ロック元プロセスを停止
taskkill /f /im Problem.exe >nul 2>&1
sc stop ProblemService >nul 2>&1
timeout /t 3 >nul

rem 2) PowerShell で長パスをまず削除試行
powershell -NoProfile -Command ^
    "Remove-Item -LiteralPath '%T%' -Force -Recurse -ErrorAction SilentlyContinue"

rem 3) まだ残っていれば robocopy /MIR で空にする
if exist "%T%\" (
    set "EMPTY=%TEMP%\empty_%RANDOM%"
    mkdir "!EMPTY!"
    robocopy "!EMPTY!" "%T%" /MIR /R:1 /W:1 /NFL /NDL /NJH /NJS /NP >nul
    rd /s /q "%T%" 2>nul
    rd "!EMPTY!" 2>nul
)

rem 4) それでも残るなら次回起動時に削除をスケジュール
if exist "%T%\" (
    echo まだ削除できません。再起動後に削除を試みます...
    rem PendingFileRenameOperations は高度な操作のため、ここでは手動対応を推奨
)

endlocal

CI/CD環境でのパス長対策

GitHub Actions・Azure DevOps・Jenkins などのCI/CD環境では、デフォルトのワークスペースが深い階層に作られることがあります。

CI/CD での短いワークディレクトリ設定
rem GitHub Actions の場合: env.GITHUB_WORKSPACE で確認
rem 対処法1: チェックアウトパスを浅くする
rem actions/checkout の with.path を "w" などに設定

rem 対処法2: バッチ内で subst を使う
subst W: "%GITHUB_WORKSPACE%" >nul 2>&1
cd /d W:\

rem 対処法3: レジストリで長いパスを有効化(管理者権限が必要)
reg add "HKLM\SYSTEM\CurrentControlSet\Control\FileSystem" /v LongPathsEnabled /t REG_DWORD /d 1 /f

最終手段の使い分けフロー

状況 推奨する最終手段
レジストリ変更不可・一時的な作業 SUBST で仮想ドライブ化
恒久的に短いパスが必要 mklink /J でジャンクション作成
深いツリーを丸ごと削除したい robocopy /MIR(空フォルダ同期)
特殊文字・日本語を含むパスの操作 PowerShell -LiteralPath
短縮名が有効な環境 dir /x で 8.3 短縮名を取得して利用
WSLが使える環境 wsl コマンドで Linux 側から操作
処理対象の階層が深すぎる 浅い一時ディレクトリにコピーして処理
ロック + 長パスの複合問題 プロセス停止 → PowerShell → robocopy /MIR の順

よくある質問

Q. subst コマンドで割り当てたドライブが再起動後に消えてしまいます。
A. subst は現在のログオンセッション限りです。再起動後も維持したい場合は mklink /J でジャンクションを作成するか、スタートアップに subst コマンドを登録してください(タスクスケジューラでログオン時に実行)。
Q. robocopy /MIR で削除しようとしたらエラーが出ました。
A. /MIR は対象フォルダの中身をソース(空フォルダ)と一致させるため、削除対象がロックされているファイルを含む場合はエラーになります。まず taskkillsc stop でロック元を止めてから実行してください。
Q. mklink /J を使うと管理者権限が必要と言われます。
A. ジャンクション(/J)とシンボリックリンク(/D、無印)の作成には管理者権限が必要です。管理者として実行するか、グループポリシーで「シンボリックリンクの作成」を一般ユーザーに許可することで回避できます。
Q. 長パスの根本原因はどこにありますか?
A. 多くの場合は「プロジェクトルートが深い場所にある」「npm install や Maven で node_modules・.m2 が生成される」「チェックアウトパスが長い」のいずれかです。C:\src\C:\a\w\ のような浅いパスをプロジェクトルートにすることが根本解です。
Q. 全手段を試してもまだ削除できないファイルがあります。
A. セーフモードで起動して削除を試みてください。セーフモードでは多くのサービス・アプリが起動せず、ロックが解除された状態になります。それでもダメな場合は Sysinternals の PsExec で SYSTEM 権限で実行する、または WinPE 環境(Windowsインストールメディアのコマンドプロンプト)から操作する方法もあります。

まとめ

通常手段でパス長制限を突破できないときの手段を整理します。

  • まず subst で仮想ドライブ化して路長を縮める(再起動で消えてよい場合)
  • 永続化が必要なら mklink /J でジャンクションを作成する
  • 深いツリーの削除は robocopy /MIR(空フォルダと同期)が確実
  • del/copy/move が失敗するときは PowerShell の -LiteralPath に切り替える
  • WSL が使える環境では Linux 側のコマンドで操作する
  • 根本解は作業ルートを C:\src\ などの浅いパスに固定すること