バッチファイルで深い階層のフォルダを操作しているとき、突然「ファイル名が長すぎます」や「The filename or extension is too long」というエラーが出て処理が止まることがあります。
これは Windows の伝統的な仕様である MAX_PATH=260文字制限 によるものです。Windows の多くの API が内部で char[MAX_PATH] サイズのバッファを使用しており、これを超えるパスを渡されると失敗します。
本記事では、この制限の仕組みを理解した上で、状況に応じた6つの対処法を実際に動くコードとともに解説します。
MAX_PATH=260 の仕組みを理解する
Windows の歴史的な制限では、パス文字列は 260文字以内(NULL終端文字含む)に収まる必要があります。実際に使えるのは 259文字ですが、ドライブ名と区切り文字を含むため、実質的にはさらに短くなります。
| 構成要素 | 文字数 | 例 |
|---|---|---|
| ドライブ名 | 3文字 | C:\ |
| ディレクトリパス | 最大235文字程度 | folder1\folder2\... |
| ファイル名 | 最大255文字(FAT系) | filename.ext |
| 合計(NULL含む) | 260文字以内 | — |
たとえば C:\(3文字)から始まるパスでは、残り 257文字 以内にフォルダ名・区切り文字・ファイル名をすべて収める必要があります。自動生成されたフォルダ名が長い場合や、プロジェクトを深い階層に置いている場合に制限に引っかかります。
・npm / pip などのパッケージマネージャが生成する
node_modules\... の深い依存関係・バックアップツールが日付・ホスト名を付けて作成する階層フォルダ
・Maven / Gradle のリポジトリキャッシュ(
.m2\repository\...)・日本語フォルダ名(1文字が複数バイトだが、パス長はバイト数でなく文字数でカウント)
6つの対処法の比較
| 対処法 | Windows 10/11 | 管理者権限 | 既存スクリプトへの影響 | 推奨場面 |
|---|---|---|---|---|
| ①レジストリでシステム全体の長いパスを有効化 | 必須(1607以降) | 必要 | 少ない(アプリ対応が必要な場合あり) | 開発PC・恒久的な解決 |
②\\?\プレフィックスを付ける |
不要 | 不要 | コード修正が必要 | 一部コマンドへのスポット対応 |
| ③robocopy / PowerShell を使う | 不要 | 不要 | コマンド変更が必要 | コピー・削除・移動 |
| ④subst で仮想ドライブを割り当てる | 不要 | 不要 | 変数変更のみ | 既存スクリプトのパスを短縮 |
| ⑤mklink でシンボリックリンクを作成 | 不要 | 通常必要 | なし | 固定パスの短縮 |
| ⑥ディレクトリ構造を浅く見直す | 不要 | 不要 | なし | 根本解決 |
対処法①:Windows 10/11 でシステム全体の長いパスを有効化
Windows 10 Anniversary Update(バージョン1607)以降では、レジストリまたはグループポリシーの設定で システム全体の MAX_PATH 制限を撤廃できます。最も根本的な解決策であり、開発者 PC では最初にこれを行うことをお勧めします。
@echo off
rem 管理者権限が必要
reg add "HKLM\SYSTEM\CurrentControlSet\Control\FileSystem" ^^
/v LongPathsEnabled /t REG_DWORD /d 1 /f
echo 長いパスのサポートを有効化しました
echo ※ 設定を反映するには再起動が必要な場合があります
# PowerShell でも同様に設定できる
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" `
-Name "LongPathsEnabled" -Value 1 -Type DWord
Write-Host "長いパスのサポートを有効化しました"
グループポリシーエディタ(gpedit.msc)からも設定できます。「コンピューターの構成」→「管理用テンプレート」→「システム」→「ファイルシステム」→「Win32 の長いパスを有効にする」を「有効」にしてください。
レジストリ設定後も以下の場合は制限が残ります。
・アプリケーション自体が長いパスに未対応(マニフェストに
longPathAware の宣言が必要)・.NET Framework 4.6.1 以前(4.6.2 以降は対応)
・古い cmd.exe 依存のコマンド(cd・dir などの一部動作)
・ネットワーク共有経由のパス
対処法②:\?\ プレフィックスを使う
\\?\ プレフィックスを付けることで、Windows の拡張長パス API(CreateFileW 等の Unicode 版 API)を直接呼び出し、最大約 32,767 文字までのパスを扱えます。レジストリ変更不要で即座に効果があります。
@echo off set "LONG=\\?\C:\very\deep\path\to\a\deeply\nested\folder\filename.txt" rem ファイルの存在確認 if exist "%LONG%" echo ファイルが存在します rem コピー copy "%LONG%" "C:\work\filename.txt" rem 削除 del "%LONG%" rem ディレクトリ作成 mkdir "\\?\C:\very\deep\new\folder"
@echo off rem 通常のUNCパス: \\server\share\path rem \\?\ + UNCは \\?\UNC\server\share\path の形式 set "UNC_LONG=\\?\UNC\server\share\very\long\path\file.txt" copy "%UNC_LONG%" "C:\work\file.txt"
・パスには相対パスが使えない(フルパス必須)
・
.(カレント)や ..(親)などの相対要素も使えない・すべてのコマンドや API が対応しているわけではない(下の表を参照)
・スラッシュ(
/)をバックスラッシュ(\)に変換してから使う| コマンド / API | \\?\ 対応 | 備考 |
|---|---|---|
copy |
◎ | 通常動作 |
del |
◎ | 通常動作 |
mkdir / md |
◎ | 通常動作 |
move |
◎ | 通常動作 |
if exist |
◎ | 通常動作 |
cd |
△ | 動作するが %CD% が不正確になる場合あり |
dir |
△ | 基本動作するが /s との組み合わせで問題あり |
xcopy |
× | 内部制限により非対応 |
robocopy |
◎(プレフィックス不要) | 標準で長いパスに対応 |
PowerShell -LiteralPath |
◎(プレフィックス不要) | 標準で長いパスに対応 |
対処法③:robocopy と PowerShell を使う
robocopy は内部で Unicode API を使用しており、\\?\ プレフィックスなしで長いパスを扱えます。copy / xcopy が失敗する場合の第一選択です。
@echo off rem robocopy は長いパスに標準対応 rem 書式: robocopy "コピー元フォルダ" "コピー先フォルダ" [ファイル名パターン] [オプション] rem 1ファイルをコピー robocopy "C:\very\deep\path\to\folder" "C:\work" "filename.txt" rem フォルダをまるごとコピー(サブフォルダ含む) robocopy "C:\src\deep\project" "D:\backup\project" /e /copyall rem 長いパスのフォルダを削除(空フォルダとの同期で削除) md "C:\empty_temp" robocopy "C:\empty_temp" "C:\very\deep\path\to\delete" /purge /e rd "C:\empty_temp" rd /s /q "C:\very\deep\path\to\delete"
robocopy は終了コードが
0(変更なし)または 1(コピー成功)のどちらも「成功」です。%ERRORLEVEL% で if errorlevel 2 から失敗として扱ってください。robocopy の詳しい使い方はファイルをコピーする方法完全ガイドを参照してください。@echo off
rem コピー(-LiteralPath で長いパスを扱う)
powershell -NoProfile -Command ^
"Copy-Item -LiteralPath 'C:\very\deep\path\file.txt' -Destination 'C:\work\'"
rem フォルダごとコピー
powershell -NoProfile -Command ^
"Copy-Item -LiteralPath 'C:\very\deep\src' -Destination 'D:\backup\' -Recurse -Force"
rem 削除(-Recurse でフォルダも再帰削除)
powershell -NoProfile -Command ^
"Remove-Item -LiteralPath 'C:\very\deep\path' -Recurse -Force"
rem 移動
powershell -NoProfile -Command ^
"Move-Item -LiteralPath 'C:\very\deep\path\file.txt' -Destination 'C:\work\file.txt'"
対処法④:subst コマンドで仮想ドライブを割り当てる
subst コマンドで深い階層のフォルダに短い仮想ドライブレター(例: Z:)を割り当てることで、既存スクリプトをほぼ変更せずにパスを短縮できます。
@echo off rem 長いパスのフォルダを Z: に割り当て set "LONG_BASE=C:\very\deep\project\src\main\resources" subst Z: "%LONG_BASE%" rem Z: を使って操作(短いパスで済む) copy Z:\config.xml C:\work\ cd /d Z:\ dir rem 処理が終わったら必ず解除する subst Z: /d echo 仮想ドライブを解除しました
@echo off
set "LONG_BASE=C:\very\deep\project\workspace"
set "VDRIVE=Z:"
rem 仮想ドライブを割り当て
subst %VDRIVE% "%LONG_BASE%"
if errorlevel 1 (
echo エラー: 仮想ドライブの割り当てに失敗しました
exit /b 1
)
rem 処理本体
call :do_work
rem 処理後に必ず解除(:cleanup へ)
:cleanup
subst %VDRIVE% /d 2>nul
exit /b
:do_work
copy %VDRIVE%\data.txt C:\work\
exit /b
・割り当ては現在のセッション(ログオン中)のみ有効(再起動で消える)
・他のユーザーや別のプロセスからは同じドライブレターが見えない場合がある
・使い終わったら
subst DRIVE: /d で必ず解除する(ゾンビドライブを防ぐ)・既存のスクリプトのパスを変数で管理していれば変数1か所の変更だけで対応できる
対処法⑤:mklink でシンボリックリンク・ジャンクションを作成する
深い階層のフォルダに対して、浅い場所にシンボリックリンクやジャンクションを作ることでパスを短縮できます。subst と異なり再起動後も残ります。
@echo off rem ジャンクション(ディレクトリへのリンク)は管理者権限不要 rem mklink /j リンク先 ターゲット mklink /j "C:\work\proj" "C:\very\deep\project\workspace\main" rem C:\work\proj がショートカットとして機能 dir "C:\work\proj" rem ジャンクションを削除(ターゲットは削除されない) rd "C:\work\proj"
@echo off rem シンボリックリンク(ファイル用) rem mklink リンク先 ターゲット mklink "C:\work\config.ini" "C:\very\deep\path\config.ini" rem シンボリックリンク(ディレクトリ用) rem mklink /d リンク先 ターゲット mklink /d "C:\work\data" "C:\very\deep\project\data"
長いパスのファイルを列挙・確認する方法
dir /s が途中で失敗する場合、PowerShell または robocopy で列挙します。
@echo off
rem 260文字を超えるファイルを一覧表示
powershell -NoProfile -Command ^
"Get-ChildItem -LiteralPath 'C:\very\deep' -Recurse -Force ^
| Where-Object { $_.FullName.Length -gt 260 } ^
| Select-Object FullName, @{N='Length';E={$_.FullName.Length}} ^
| Sort-Object Length -Descending ^
| Format-Table -AutoSize"
@echo off rem /l オプションで実際にはコピーせず一覧だけ表示 robocopy "C:\very\deep\source" "C:\dummy" /l /e /256 rem /256 オプションで長いパスのサポートを明示的に有効化(Windows 10対応版)
長いパスのファイルを安全に削除する方法
del や rd /s が失敗する場合は robocopy の /PURGE を使います。
@echo off
rem 削除したいフォルダ: C:\very\deep\target
rem 空のテンポラリフォルダを用意
set "EMPTY_DIR=C:\empty_for_robocopy_%RANDOM%"
mkdir "%EMPTY_DIR%"
rem 空フォルダと同期(/purge で対象側に余分なファイルを削除)
robocopy "%EMPTY_DIR%" "C:\very\deep\target" /purge /e
rem テンポラリフォルダとターゲットを削除
rd "%EMPTY_DIR%"
rd /s /q "C:\very\deep\target"
if not exist "C:\very\deep\target\" (
echo 削除完了
) else (
echo エラー: 削除に失敗しました
)
エラーメッセージと対処法の対応表
| エラーメッセージ | 原因 | 推奨対処法 |
|---|---|---|
| ファイル名が長すぎます | MAX_PATH 超過 | 対処法①②③いずれか |
| The filename or extension is too long | 同上(英語版 Windows) | 同上 |
| 指定されたパスが見つかりません | 長いパスをコマンドが処理できない | \\?\プレフィックス or robocopy |
| The system cannot find the path specified | 同上(英語版) | 同上 |
| コピー先フォルダが見つかりません | コピー先もパス長超過の場合 | コピー先を浅い場所に変更 |
| xcopy が途中で失敗する | xcopy は長いパス非対応 | robocopy に切り替え |
実践スクリプト:長いパスのファイルを安全にバックアップ
深いディレクトリ構造のプロジェクトを日付付きバックアップフォルダへコピーする実用的なスクリプトです。robocopy を使い、長いパスに対応しています。
@echo off
setlocal enabledelayedexpansion
if "%~1"=="" (
echo 使い方: backup_longpath.bat "バックアップ元" "保存先ベース"
echo 例: backup_longpath.bat "C:\deep\project" "D:\backup"
exit /b 1
)
set "SRC=%~1"
set "DST_BASE=%~2"
if not exist "%SRC%\" (
echo エラー: バックアップ元が見つかりません: %SRC%
exit /b 1
)
rem タイムスタンプ生成
for /f "tokens=1-3 delims=/" %%a in ("%DATE%") do set "D=%%a%%b%%c"
for /f "tokens=1-2 delims=:." %%a in ("%TIME: =0%") do set "T=%%a%%b"
set "DEST=!DST_BASE!\backup_!D!_!T!"
echo バックアップ開始
echo 元: %SRC%
echo 先: !DEST!
rem robocopy で長いパスに対応したコピー
robocopy "%SRC%" "!DEST!" /e /copyall /256 /r:3 /w:5
rem robocopy: 0=変更なし 1=コピー成功 → どちらも成功
if %errorlevel% leq 1 (
echo バックアップ完了: !DEST!
) else (
echo エラー: バックアップ中に問題が発生しました (code=%errorlevel%)
exit /b 1
)
endlocal
よくある質問(FAQ)
\\?\プレフィックス(②)、robocopy / PowerShell の利用(③)、subst による仮想ドライブ(④)が使えます。どれも一般ユーザー権限で実行可能です。xcopy は \\?\ 非対応です。robocopy または PowerShell の Copy-Item -LiteralPath に切り替えてください。cd は動作しますが %CD% の値が意図しない形式になる場合があります。if %errorlevel% leq 1 (成功処理) else (失敗処理)のように判定してください。subst の割り当てはセッション内のみ有効です。永続的に使いたい場合は、スタートアップフォルダにバッチを置くか、mklink /j によるジャンクション(④)を使ってください。Remove-Item -LiteralPath "パス" -Recurse -Force を使ってください。まとめ
Windowsのパス長制限(MAX_PATH=260文字)への対処法をまとめます。
- 恒久的な解決: レジストリで
LongPathsEnabled=1を設定(Windows 10/11・管理者権限必要) - スポット対応:
\\?\プレフィックスを付けてAPI経由で長いパスを処理 - コピー・移動・削除:
xcopyの代わりにrobocopyまたは PowerShell の-LiteralPathを使う - 既存スクリプトの改修なし:
substで仮想ドライブを割り当ててパスを短縮 - 削除できないフォルダ: robocopy
/purgeで空フォルダと同期して削除 - 根本解決: ディレクトリ構造を浅くする・フォルダ名を短縮する
バッチファイルでのパス操作全般についてはパスのスペースエラー解決ガイドも合わせて参照してください。ファイルのコピーや削除コマンドの詳細はファイルをコピーする方法完全ガイド・ファイルを削除する方法完全ガイドをご覧ください。

