「Windowsのパス長さ制限に関して」の版間の差分
(Notion-MW) |
(Notion-MW) |
||
183行目: | 183行目: | ||
ただし、逆にWindowsにない制限として、Unixではそれぞれの<strong>ファイル名の長さ上限が255バイト</strong>という制限がある(Windowsと違って<strong>パス長さ</strong>の制限は4096なので、長さ200バイトの名前のフォルダの中に長さ200バイトの名前のフォルダを作るにあたっては不自由がない)。また、エンコードはUTF-8を基準に計算されるので、3バイトで表現される通常の日本語の文字(ひらがなや漢字)だと85文字、U+10000を超えるような文字(𩸽など)だと4バイト分なので63文字しか入らない。Windows上ではこれらを超える長さのフォルダを作れるが、Git Bashではそこにcdできないということになる。 | ただし、逆にWindowsにない制限として、Unixではそれぞれの<strong>ファイル名の長さ上限が255バイト</strong>という制限がある(Windowsと違って<strong>パス長さ</strong>の制限は4096なので、長さ200バイトの名前のフォルダの中に長さ200バイトの名前のフォルダを作るにあたっては不自由がない)。また、エンコードはUTF-8を基準に計算されるので、3バイトで表現される通常の日本語の文字(ひらがなや漢字)だと85文字、U+10000を超えるような文字(𩸽など)だと4バイト分なので63文字しか入らない。Windows上ではこれらを超える長さのフォルダを作れるが、Git Bashではそこにcdできないということになる。 | ||
<span id="webdav"></span> | |||
=== WebDAV === | |||
Windowsでは、<code>host:port</code>において稼働しているWebDAVサーバーの<code>my/folder</code>というフォルダに、<code>\\host@port\my\folder</code>あるいは<code>\\host@port\DavWWWRoot\my\folder</code>というパスでアクセスできる。ただし前者は<strong>トップディレクトリに関しては機能しない</strong>(アクセスできませんと言われる)ので注意。とはいえ後者はexplorerのアドレスバーが<code>http://host:port/my/folder</code>というHTTP形式の表示になってしまうのが扱いづらい。 | |||
そして、WebDAVに関しても(詳細は調べていないが)260文字程度以上の長さのフォルダにはアクセスできない。しかも、<code>\\?\UNC</code>や(ネットワークドライブを割り当てたとして)<code>\\?\</code>を付けてもアクセスできず、どうやっても表示する方法はわからなかった。また、260文字程度の長さになるファイルも表示されなくなるなどした。 | |||
長いパスのファイルを含むWebDAVサーバーをまともに使うのであれば、WinFSPなどでマウントした方が良さそうである。 | |||
== 結論 == | == 結論 == |
2024年4月1日 (月) 11:08時点における最新版
Windowsが扱えるパスの長さは260文字(定数MAX_PATH
)に制限され、それ以上の長さのパスをもつファイル・フォルダの操作に支障が出ることがある。
- これはあくまでWindowsの問題であってファイルシステム(NTFS)の制限ではないので、後述の通りエクスプローラ上でも例えばパス長さ260を超えるファイルを作ること自体は容易である。ネットワーク上のフォルダを閲覧する場合など、実際にWindows上で長さ260を超えるパスを扱う必要がある場面も多く、ダブルクリックで開く程度の操作はできることが多い。
以下ではこの問題に関してさらに詳細な挙動を解説する。なお動作確認に使用したのは主にWindows10である。Windows11でも少し試したが、大幅な改善はなさそうである。
関連する公式のドキュメントとしては以下が参考になる。
正確な数え方・内訳
260の内訳は以下のようになっている。
ドライブレター(1)+コロン(1)+パス本体(256)+終端のヌル文字(1)
従って我々が普段「フルパス」として認識している部分の長さ上限は実際には259ということになる。
これに加えて、カレントディレクトリ(作業ディレクトリ)のパスの上限は258のようである(コマンドプロンプトやPowerShellでcdしようとしてみた結果)。おそらくディレクトリ末尾の\
を含めて259文字以下ということかと思われるが、確実な情報はあまりない。さらに、cmdでは、ちょうど258文字のフォルダにはcdできても中でdirできなかった。
また、この260文字というのはUTF-16で数えた値である。従って「a」や「字」などのU+FFFFまでのUnicode文字は1文字、「𩸽」などのU+10000を超える文字はサロゲートペアで表現されるため2文字とカウントする。
制限の解除
この制限は最近のWindowsならLongPathsEnabledというレジストリの値を1に設定することで解除できるとされている。例えばPowerShell上で以下を実行すればよい。
Set-ItemProperty "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem" -Name LongPathsEnabled -value 1
また、現在の値を確認するには以下を実行する。
(Get-Item -Path "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem").GetValue("LongPathsEnabled")
しかし、制限を解除すれば全ての操作が支障なくできるというわけではない。例えば、Windows APIのマルチバイト文字方式の関数(末尾がAのもの)は長いパスに対応していない(末尾がWのワイド文字方式のものは対応している)らしく、これを使用しているアプリケーションはこの制限を解除しても正しく動かない。またエクスプローラ上での操作性もほとんど改善しない。
パスの形式について
今後出てくる様々な挙動を把握するために、ファイルに短い別名を与える仕組みである「8.3形式」およびパス長さ制限の回避に有効な「”\\?\
”プレフィックス」について紹介する。
8.3形式(短いファイル名)
古いWindowsのファイル名には「8文字以下の名前+ピリオド+3文字以下の拡張子」という長さ制限があった。また、空白を含む一部の記号も使用できない。この制限がなくなった(緩和された)現在でも、Windowsは制限を満たさないファイルに対してこの制限に基づく別名を自動生成する。これが「8.3形式」あるいは「短いファイル名」などと呼ばれるものである。例としては”Program Files”→”PROGRA~1” などがある。8.3形式の名前はコマンドプロンプトのdir /xコマンドで表示できる。
比較的新しいWindowsでは、fsutil 8dot3name
コマンドを使うことで、8.3形式の名前を生成するかどうかをシステム全体およびボリューム(Cドライブ、Dドライブ)ごとに設定できる。デフォルトではCドライブのみ有効になっているようである。
この別名を通常の名前のかわりに使用することで、現実的に多くの場合は長さ制限を回避できるだろう(もちろん階層が深すぎて「短いパス」でも長さ制限を超えてしまう場合などはできない)。また、パスに含まれる空白による問題も回避できる。
後述の通り一部アプリケーションはこれを積極的に使うことで長さ制限を回避しようとすることがある。この際は8.3形式を可能な限り多く使用したパス(可能な限り最も短いパス)が用いられるようである。
例えば8.3形式の名前がCでは有効、Dでは無効(つまりデフォルト設定)だったとする。Dドライブ直下に”longfoldername”という名前で”C:”へのジャンクションを作り、それを経由してC:\Program Filesにアクセスする場合は、”D:\longfoldername\PROGRA~1”が「可能な限りもっとも短いパス」ということになる。
- 「可能な限り多く」ではなく、短い名前をもつフォルダのうち一部のみを8.3形式としたパスがexplorerのアドレスバーなどで使われることもたまにあるようだが、詳しい条件は不明である。
8.3形式は基本的には普通のパスと同じ扱いであるため、多くのアプリケーションが対応しているが、PowerShellなどは強制的に長い名前に変換しようとするため支障が出ることもある(後述)。また、4文字以上の拡張子は3文字に短縮されてしまうため、例えばWordの.docxファイルが.docファイルと混同されるなどの問題が生じる。また他にもシェルの入力補完で表示されないなど、通常のパスと比べて不便であることには変わりない。
“\\?\
”プレフィックス
公式ドキュメントでも用語の定義がはっきりしないのでよくわからないが、パスの先頭に”\\?\
”(検索しやすさのために書いておくと、バックスラッシュ(=円記号)2つ+クエスチョンマーク+バックスラッシュ1つ)というプレフィックスを付加すると”Win32 file namespace”というものにアクセスすることになるらしい。具体的にどうなるかというとWindows APIによるパスに対する各種のチェックが行われなくなり、結果として長さ制限を超えるパスを使えるようになる。ただし対応状況はアプリケーションによってまちまちである。
このパスはネットワーク上のファイルを指定するのに使われるUNCパス(UNCはUniversal Naming ConventionあるいはUniform Naming Conventionの略)の一種とも考えられる。UNCパスは「\\コンピュータ名\パス本体
」という形式だが、この「コンピュータ名」の部分に?
が入っているというわけである。ちなみに、元々UNCパスだったものにこのプレフィックスを付加する場合は\\?\UNC\コンピュータ名\パス本体
とすれば良いらしい(エクスプローラーでそのように表示されている)。
なお、クエスチョンマークの代わりにピリオドを使用した\\.\
というプレフィックスもあり、これだと”Win32 file namespace”ではなく”Win32 device namespace”というものにアクセスすることになるらしいが、詳細は不明。長さ制限を回避するには使えないようである。
以降では便宜的に「“\\?\
”プレフィックス」を指して単に「プレフィックス」と呼ぶ。
ちなみに今回の話題とは無関係だが、con
やnul
などWindows上で許容されない名前のファイルが削除できない際に、コマンドで\\?\
や\\.\
を付けたパスを指定すると削除できる。
Explorerや各シェル、アプリケーションにおける挙動の変化
ここから具体的な挙動の変化を見る。特に記載がないものは、レジストリ値を変更して制限を解除しても変わりがないということである。
以下で「PowerShell」と呼んでいるのはバージョン5であって、6以降(実行ファイル名がpwsh.exeになって以降)では異なるようである。
8.3形式・プレフィックス付きパス等を扱えないアプリケーション
アプリケーションによって細かい挙動は様々だが、プレフィックス付きのものや8.3形式の名前が含まれるパスに関して不具合が出ることがある。はっきりエラーメッセージが出ることもあれば、何も起こらないこともあるし、空白の(ファイルを開いていない状態の)ウインドウが表示される場合もある。いくつか例を挙げておく。
- Windows Media Player…プレフィックス付きのものや長さ制限を超えるパスを引数に渡すと起動しない(何も起こらず終了する)。
- ペイント…プレフィックス自体は扱えるが長さ制限を超えるとプレフィックス付きであっても扱えない(引数として渡すと何も起こらず終了する)。
- Word…プレフィックス付きパスを渡すと、起動はするが新規作成の画面になる。また長さ制限を超えるパス(プレフィックス付き含む)を渡すと「パス名が長すぎる」というようなメッセージを表示した上で新規作成の画面になる。また、”.docx”は拡張子が4文字以上であるため、短いパスとして渡されると不自然な動作になることがある(”.doc”、つまりWord 97-2003文書との混同が生じる)。
- Visual Studio…8.3形式を用いて短くした.slnや.vcxprojを渡しても、元の(8.3形式を使わない)パスが制限を超えていると「パス名が長すぎる」というようなエラーが表示され開けない。
長さ制限を超えるパスをカレントディレクトリとして実行できないWindowsネイティブアプリケーション(?)
おそらくアプリケーション側の都合で、長さ制限を超えるパスをカレントディレクトリとしての起動に対応していないことがある。具体的には、Git Bashで、長い名前をもつフォルダを開いているとき、mspaintやcmdの実行に失敗する。次のようなメッセージが出る。
Error: Current working directory has a path longer than allowed for a
Win32 working directory.
Can't start native Windows application from here.
bash: /c/Windows/system32/mspaint: File name too long
しかし、同じ状況でgrep.exeの実行は成功する。従ってアプリケーションによって対応状況に差があるものと推測される。ただしこのエラーメッセージはGit Bashによるものであり、またGit Bash以外で違いを確認できていない(例えばcmdやPowerShellではmspaintもgrepもシェル側の都合で失敗する)ので、例えばGit Bashが必要以上に多くのプログラムを勝手に”native Windows application”に分類している可能性などは残る。またgrep.exeのようなGit Bash付属の実行ファイル以外で起動できるものは確認していないので、判定条件がよくわからない。
カレントディレクトリを8.3形式によって短くできれば起動できる。
explorer
まず、以下の挙動については全てのフォルダで同様。8.3形式で短くできるものも含まれる。
- 長さ制限を超えるファイルのリネームができない(元々超えている場合は短くすることもできない)
- 超えていない場合、制限いっぱいになるとそれ以上入力できなくなる
- 中にあるファイルのパス長が制限を超えるようにフォルダをリネーム・移動することは可能
- これにより長さ制限を超えるパスをもつファイル・フォルダをエクスプローラ上で容易に作れる。逆に、長すぎてリネームできないファイルなども、外側のフォルダをいったん短い名前に変える・短い名前の場所に移動することで操作できるようになる
- 長さ制限を超えてもフォルダを開くことは可能。
さて、explorerは、制限を超える長いパスに対しては概ね次のような原則で対処しようとするようである。
- 8.3形式の名前を使用して「可能な限り最も短いパス」を得て、これを使う。
- 8.3形式の名前がない、あるいはそれを使っても制限を超過するなどの場合は、パス先頭に”
\\?\
”プレフィックスを付加する。
例えばexplorerのアドレスバーを見ると、このように動作していることがわかるだろう。あるいは、そのフォルダおけるexplorerの挙動はアドレスバーを見れば大体予測できるということにもなる。ファイルに関して調べたければ「パスのコピー」を使うとよい。なお、手動でプレフィックスや8.3形式の名前をエクスプローラのアドレスバーに入力して移動した際は、通常のパスに変換される(それが長さ制限を超えていれば改めて上記の対処が行われる)。
ただし上記1の部分では、(末尾の
\
を含めない)パス長さが260文字以上になったときのみ8.3形式が適用される。しかし、実際にはカレントディレクトリのパス長さ上限は258文字である。よって、ちょうど259文字の長さのパスでは、アドレスバーには通常のパスが表示されるにもかかわらずアドレスバーにcmdと打っても正しく起動しないという、258文字以下または260文字以上のフォルダでは見られない挙動をする。1とは違って、2の部分では、8.3形式を使ったとしてもパス長さが259文字以上になると
\\?\
が付加されるのでこのような問題は起こらない。
1のとき
ダブルクリックでファイルを開くことができるほか、右クリックメニューの「送る」「Codeで開く」(※VS Codeを適切な設定でインストールすると表示される)なども正常動作する。なお「PowerShell ウインドウをここで開く」は、動作するが、PowerShellは強制的に通常の(長い)名前を使おうとするため、カレントディレクトリとしては通常の名前が表示される(この先の挙動はPowerShellの項目を参照)。また、PowerShellを使用するよう設定したWindows Terminalをここで開くと、制限を解除した場合は通常形式のパスをカレントディレクトリとして正しく起動するが、解除しない場合はカレントディレクトリをC:\WINDOWS\System32\WindowsPowerShell\v1.0
として起動する。
ファイルの新規作成は可能だが、フォルダを作成しようとするとエラーが出る。ファイルやフォルダを「ごみ箱に移動」できる。
2のとき
2に当てはまるフォルダでは、「Codeで開く」など外部プログラムを起動するような右クリックメニューの多くが動作しない(Explorer.EXEによる”ディレクトリ名が無効です。”とのダイアログが出る)。「開く」も動作せずファイルをダブルクリックで開くこともできない(例えば.txtがメモ帳に関連付けられた状態で開いてみるとよい)(※Windows11ではメモ帳の動作が改善しているのでCOM実装に変更されたようだが、一般のプログラムではうまくいかない。ただしエラーダイアログは出なくなり、無言で起動しない)。ただしCOMを用いて実装されているメニュー(レジストリにコマンドラインではなくUUIDが書かれているようなもの)は正常に動作するようである(例えば「映画 & テレビ」「フォト」のようないわゆる「アプリ」で開く場合)。
また、以下はCOMが用いられているか不明(未調査)だが一定のまともな挙動を示すためその可能性が考えられる。
- 「PowerShell ウインドウをここで開く」…正常に動作する。
- 「プログラムから開く」…「開く」とは異なり、(アプリケーション側が対応していれば)正常に動作する。
- 「送る」…Explorer.EXEのエラーは表示されない。ただし、特殊な方法(これもCOM?)で実装されている「ドキュメント」「デスクトップ (ショートカットを作成)」「圧縮 (zip 形式) フォルダー」メニューなどを除けば、選択しても何も起こらない。また、zipのメニューに関しても、最終的にzip作成には失敗する。なお、この「送る」に関しては、(2に当てはまるフォルダ内でなくとも)2に当てはまるファイルが一つでも含まれていると同様に動かないようである。
また、「新規作成」メニューにはフォルダの作成のみ表示されるが、なぜか”管理者マーク”が付いており、押しても何も起こらない。従ってファイルもフォルダも作れない。「ごみ箱に移動」は動作せず、delキーを押しただけで「完全に削除しますか?」と聞かれる(完全に削除することは可能)。
コマンドプロンプト
制限を解除した場合に限り長いパスを扱える(cdやdirが動作する)。また8.3形式の名前も普通に使える。
プレフィックスは、全く解釈できないわけではない(例えばrdコマンドの引数に使うことはできる)が、カレントディレクトリとしては許容されないようで、指定してcdしようとすると「CMD では UNC パスは現在のディレクトリとしてサポートされません。」と言われる。
外部プログラムを実行する際は、(制限が解除されていても)カレントディレクトリのパスが長いと判定したら「可能な限り最も短いパス」を代わりに使うようで、(それで制限以下の長さにできるフォルダであれば)正しく起動できる。
PowerShell
- ここに書いているのは主にPowershell 5.0あたりの話で、最近のPowerShell(PowerShell Coreと呼ばれたり、実行ファイル名がpwsh.exeだったり)では色々と改善が見られる。
制限を解除した場合に限り長いパスを扱える点はcmdと同じである。しかし前述の通り、カレントディレクトリに8.3形式が使われるのを許容しないようで、強制的に長い名前に変換するため、しばしば奇妙な挙動を引き起こす。
例えば、カレントディレクトリのパスが200文字だったとして、そこに名前が100文字のフォルダがあったとする。いずれも実体はCドライブにあって8.3形式の名前が有効とする。このとき、パス長さ制限が解除されていなくても、8.3形式の名前を使えばcd自体はできる。しかしcdが成功すると同時にカレントディレクトリは長い名前に変換されるため、カレントディレクトリは300文字程度になり制限を超えてしまい、そこでのdirコマンドなどの実行は失敗するようになってしまう。
また、長さ制限を超えるパスがカレントディレクトリであるときに外部プログラムを実行すると、制限が解除されていない場合は「可能な限り最も短いパス」を使おうとするが、解除されている場合は長いパスをそのまま使おうとする(cmdとの違い)。結果として実行に失敗する。つまり、制限を解除した結果としてむしろ実行が失敗するようになるという状況が発生している。
プレフィックスには対応していて基本的な操作ができる。カレントディレクトリに設定するとMicrosoft.PowerShell.Core\FileSystem::\\?\C:\Users
のように表示される。
またカレントディレクトリがプレフィックス付きであるときに外部プログラムを実行するときは、不要な\\?\
や\\.\
を除去してくれるようで、正しく起動できる。これは\\?\C:\Windows
のようなフォルダで正しく起動できるということであって、そもそも\\?\
を付けないと扱えないような長いパスでの実行はいずれにしても失敗する。
また、(制限を解除していない状態だとしても)この際に「最も短いパス」を使用することまではしてくれないようで、Cドライブ上の長いパスに\\?\
を付けたものをカレントディレクトリとしてnotepadを実行してみたが失敗した。
また、なぜかCドライブ直下を対象にしたcd \\?\C:
やcd \\.\C:
はうまくいかない(cd \\?\C:\Users
やcd \\.\C:\Users
は成功する)。\\.\
に関しては一旦cd \\.\C:\Users
などしてからcd ..
をするとCドライブにいけるがこのときはなぜか\\.\
が消えてMicrosoft.PowerShell.Core\FileSystem::C:
という表示になる。\\?\
の場合はこれもできない。
また、プレフィックス付きパスにいるときにcdで相対パスを使用する際は8.3形式も使えるが(強制的に長い名前に変換されるのは同じ)、\\?\
と8.3形式を組み合わせた絶対パスを用いると失敗する場合もある。具体的には、Cドライブ直下のフォルダ(たとえばcd \\?\C:\PROGRA~1
など)は成功したが、他ではうまくいかない。なお、cd \\?\C:\PROGRA~1
を実行した際はなぜか通常のパスへの変換が行われず、カレントディレクトリがMicrosoft.PowerShell.Core\FileSystem::\\.\C:\PROGRA~1
となる。詳細不明。
また\\?\
のかわりに\\.\
を用いるとCドライブ直下以外でもcdに成功したが、前述の通り\\.\
にはパス長さ制限を回避する効果がないようなので実用性は不明である。またCドライブ直下のフォルダのみ8.3形式が許容されるようで、cd \\.\C:\PROGRA~1\INTERN~1
とするとカレントディレクトリはMicrosoft.PowerShell.Core\FileSystem::\\.\C:\PROGRA~1\Internet Explorer
になる。
Git Bash
git bashはUnix由来のシェルであるためか、それ自体で長さ制限を超えるパスを扱う能力を持っているようである。従って制限を解除しなくても長いパスを扱える。また8.3形式の名前も使える。
「可能な限り最も短いパス」を得ようとするような動作はしないため、長いパスをもつカレントディレクトリでの外部プログラム実行は失敗する(前述の通り、Git Bash内蔵のgrep.exeなどは例外)。
Git Bashでは、\\?\
や\\.\
や//./
は使うとエラーになり、//?/
を付けた場合はcdには成功するが//?/
が除去される。
ただし、逆にWindowsにない制限として、Unixではそれぞれのファイル名の長さ上限が255バイトという制限がある(Windowsと違ってパス長さの制限は4096なので、長さ200バイトの名前のフォルダの中に長さ200バイトの名前のフォルダを作るにあたっては不自由がない)。また、エンコードはUTF-8を基準に計算されるので、3バイトで表現される通常の日本語の文字(ひらがなや漢字)だと85文字、U+10000を超えるような文字(𩸽など)だと4バイト分なので63文字しか入らない。Windows上ではこれらを超える長さのフォルダを作れるが、Git Bashではそこにcdできないということになる。
WebDAV
Windowsでは、host:port
において稼働しているWebDAVサーバーのmy/folder
というフォルダに、\\host@port\my\folder
あるいは\\host@port\DavWWWRoot\my\folder
というパスでアクセスできる。ただし前者はトップディレクトリに関しては機能しない(アクセスできませんと言われる)ので注意。とはいえ後者はexplorerのアドレスバーがhttp://host:port/my/folder
というHTTP形式の表示になってしまうのが扱いづらい。
そして、WebDAVに関しても(詳細は調べていないが)260文字程度以上の長さのフォルダにはアクセスできない。しかも、\\?\UNC
や(ネットワークドライブを割り当てたとして)\\?\
を付けてもアクセスできず、どうやっても表示する方法はわからなかった。また、260文字程度の長さになるファイルも表示されなくなるなどした。
長いパスのファイルを含むWebDAVサーバーをまともに使うのであれば、WinFSPなどでマウントした方が良さそうである。
結論
こうしてみると長さ制限を解除しても大した改善はないというのが実情である。