从Python中使用Win32长路径

5
注意:本问题涉及新支持的win32长路径(自Windows 10版本1607,Build 14352可用),而不是以\\?\开头的扩展UNC路径。
我通过组策略启用了长路径支持并重新启动了我的电脑。我在注册表中检查了两个键值 HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\FileSystem\LongPathsEnabledHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled,它们都设置为1
然后我打开了Python REPL并尝试创建一个超过260个字符限制的目录,但失败了。
>>> import os

>>> longdirname = 'a' * 300

>>> longdirname
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'

>>> os.makedirs(longdirname)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "c:\python27\lib\os.py", line 157, in makedirs
    mkdir(name, mode)
WindowsError: [Error 3] The system cannot find the path specified: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
[Error 3] The system cannot find the path specified: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'

我假设长路径支持在这里没有生效。为什么会这样,如何才能正确启用它,以便从Python脚本中使用?

更新:我还尝试了通过pywin32直接调用一个Win32 API函数,在其文档中声称它应该支持长路径,但仍然失败了:

>>> import win32file

>>> longname = 'a' * 300

>>> win32file.CreateDirectoryW(longname, None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
error: (3, 'CreateDirectoryW', 'The system cannot find the path specified.')
(3, 'CreateDirectoryW', 'The system cannot find the path specified.')

更新2:还尝试通过单独的组件创建:

>>> for i in range(1, 300):
...     win32file.CreateDirectoryW('a', None)
...     os.chdir('a')
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
error: (206, 'CreateDirectoryW', 'The filename or extension is too long.')
(206, 'CreateDirectoryW', 'The filename or extension is too long.')

(os.makedirs在这种情况下也不起作用)


更新3:以下批处理脚本将不会失败:

@echo off

setlocal enableextensions enabledelayedexpansion

pushd

set /a count = 1
for /L %%i in (1,1,300) do (
  mkdir a
  cd a
  echo %%i
)
endlocal

popd

所以我假设这与Python有关。

1
我在想,名称本身是否有限制。你尝试过使用代表树形结构(用斜杠分隔)的长目录名吗? - Jean-François Fabre
@Jean-FrançoisFabre 建议不错,我很兴奋地尝试了一下,但不幸的是它并没有起作用 - 请看我在帖子中的更新。 - Tamás Szelei
你在尝试使用Python之前,是否尝试过在经典CMD中运行? - Jean-François Fabre
@Jean-FrançoisFabre 再次给出了很好的建议,使我们更接近解决方案:批处理脚本可以轻松创建一个名为a的300级深度目录路径。请查看我的更新。 - Tamás Szelei
1
如果没有人回答,今晚我会尝试一下。我在家里有管理员权限 :) - Jean-François Fabre
显示剩余2条评论
1个回答

3
解决方案既简单又有些令人失望:Python 3.6之前的版本无法利用长路径。3.6 changelist

Windows历史上将路径长度限制为260个字符。这意味着,超过此长度的路径将无法解析,将导致错误。

在最新版本的Windows中,该限制可扩展到约32,000个字符。管理员需要激活“启用Win32长路径”组策略,或将注册表值HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem@LongPathsEnabled设置为1。

这使得open()函数、os模块和大多数其他路径功能能够接受并返回使用字符串时长于260个字符的路径。(在Windows上不推荐使用字节作为路径,并且当使用字节时该功能不可用。)

更改以上选项后,无需进行其他配置。

从版本3.6开始更改:支持Python的长路径。

为了证明它的有效性,我执行了以下操作:

Python 3.6.2 (v3.6.2:5fd33b5, Jul  8 2017, 04:14:34) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> longdir = 'a' * 300
>>> os.makedirs(longdir)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\tamas\AppData\Local\Programs\Python\Python36-32\lib\os.py", line 220, in makedirs
    mkdir(name, mode)
OSError: [WinError 123] The filename, directory name, or volume label syntax is incorrect: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
>>> for i in range(1, 300):
...     os.mkdir('a')
...     os.chdir('a')
...
>>> len(os.getcwd())
631
>>>

第一个 makedirs 调用失败,因为单个组件仍然被限制在 255 个字符以内。

显然,此部分文档需要更新。在Windows上,字节路径不再被弃用。3.6版本将Windows文件系统编码硬编码为UTF-8,并且内部字节路径被转换为UTF-16LE宽字符字符串。 - Eryk Sun
在3.6中,通过向清单添加“longPathAware”条目来启用此功能。MSDN并没有非常清楚地说明必须在注册表和应用程序清单中都启用此功能。 - Eryk Sun
@eryksun,我曾经读到过Windows实际上并没有检查注册表设置,只检查清单。您是否确定注册表设置是否真的必要? - Harry Johnston
@HarryJohnston,是的,两者都是必需的。kernelbase.dll的初始化首先调用kernelbase!BasepIsProcessLongPathAwareByPolicy,该函数检查“LongPathsEnabled”注册表设置。如果它被策略启用,则接下来调用kernelbase!BasepIsProcessLongPathAwareByManifest,该函数检查应用程序清单中的“longPathAware”设置。如果应用程序支持长路径,则会在PEB中启用IsLongPathAwareProcess字段。 - Eryk Sun
@eryksun,谢谢。希望在未来的某个Windows 10版本中,注册表设置将默认开启。 - Harry Johnston
有趣的是,我从文档中的印象是清单可以用于按应用程序启用该功能,而注册表键可以用于全局启用它。如果不是这种情况,那么措辞就相当模糊了。幸运的是,这解决了我的问题,并可能成为未来通过谷歌到达此处的人们的有用资源。 - Tamás Szelei

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接