标题: "文件名或扩展名太长"导致的文件删除失败 创建: 2020-06-18 20:00 更新: 2021-12-07 15:58 链接: https://scz.617.cn/windows/202006182000.txt -------------------------------------------------------------------------- 目录: ☆ 问题描述 ☆ 问题复现 ☆ 问题起因 ☆ 问题解决 1) subst 2) \\?\前缀(有条件成功) 2.1) Win10 1709的困惑 2.2) 尾部有空格的文件名 2.3) 关于\\?\的其他讨论 3) powershell(失败) 4) 第三方工具或自编程(未测试) ☆ 网友反馈 0) 陆麟的评论 1) 利用8.3文件名删除畸型文件 1.1) 无法利用8.3文件名重命名、复制畸型文件 2) rd命令删除畸型目录树 3) 利用压缩软件删除畸型目录树 4) robocopy 5) del命令删除畸型文件 -------------------------------------------------------------------------- ☆ 问题描述 某人说Win8有两个文件夹无法删除,起初以为是NTFS损坏所致,结果是目录树中某些 文件名超长所致。按她的说法,其学生通过微信发过来论文资料压缩包,她解压后形 成的目录树。我在cmd中尝试del、rename,均失败。 稍微研究了一下这个问题,发现Win7、Win8相对棘手,Win10有所改进。本文不考虑 用LiveCD启动Linux的解决方案。 ☆ 问题复现 在Win7上复现问题。 mkdir c:\long_path_test\evil cd c:\long_path_test\evil echo "scz is here" > evil.txt rename evil.txt 0000000000000000111111111111111122222222222222223333333333333333444444444444444455555555555555556666666666666666777777777777777788888888888888889999999999999999aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccddddddddddddddddeeeeeeeeeeeeeeee.txt 长文件名非扩展名部分由如下行构成: 0000000000000000 1111111111111111 2222222222222222 3333333333333333 4444444444444444 5555555555555555 6666666666666666 7777777777777777 8888888888888888 9999999999999999 aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbb cccccccccccccccc dddddddddddddddd eeeeeeeeeeeeeeee rename失败,报错: 系统无法接受请求的路径或文件名。 // 中文Win7 The system cannot find the path specified. // 英文Win10 1709 用subst弄个虚拟盘符"T:",切换到T盘,再次rename。 subst t: %cd% t: rename evil.txt 00...ee.txt type 00...ee.txt rename成功,type可以正常显示文件内容。切回C盘根目录,删除虚拟盘符: c: subst t: /d cd \ 在资源管理器中双击"00...ee.txt",提示: 文件名或扩展名太长。 试图删除"c:\long_path_test"时报错: 文件名对目标文件夹可能过长。您可以缩短文件名并重试,或者尝试路径较短的位置。 在资源管理器中试图直接删除"00...ee.txt",报同样的错;其右键菜单没有"删除"、 "重命名"等。选中"00...ee.txt",按F2试图改名,没反应。此时先打开notepad,再 通过"文件->打开"去查看"c:\long_path_test\evil\"目录,文件对话框中干脆不显 示"00...ee.txt",就跟空目录一样。 假设通过前述办法制造了删不掉的"00...ee.txt",用7-Zip打包long_path_test目录 成long_path_test.7z。去别处解压long_path_test.7z,立即复现问题。 https://scz.617.cn/windows/long_path_test.7z ☆ 问题起因 Windows有个著名常量MAX_PATH,等于260。很多文件操作会检查路径长度是否超过该 值,该检查与文件系统是否支持超长文件名无关,与使用FAT32或NTFS无关。NTFS本身 支持长达32767宽字符的路径名。 参看: -------------------------------------------------------------------------- Long Filenames on Windows - John Rennie [2009-05-17] http://www.ratsauce.co.uk/notablog/longfilenames.asp CreateFileA function https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea Naming Files, Paths, and Namespaces https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file (这篇概括得不错,推荐无基础入门者阅读) -------------------------------------------------------------------------- CreateFileA的描述中有: -------------------------------------------------------------------------- lpFileName The name of the file or device to be created or opened. You may use either forward slashes (/) or backslashes (\) in this name. In the ANSI version of this function, the name is limited to MAX_PATH characters. To extend this limit to 32767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path. For more information, see Naming Files, Paths, and Namespaces. Starting with Windows 10, version 1607, for the unicode version of this function (CreateFileW), you can opt-in to remove the MAX_PATH limitation without prepending "\\?\". See the "Maximum Path Length Limitation" section of Naming Files, Paths, and Namespaces for details. -------------------------------------------------------------------------- "Maximum Path Length Limitation"中写有: -------------------------------------------------------------------------- In the Windows API, the maximum length for a path is MAX_PATH, which is defined as 260 characters. A local path is structured in the following order: drive letter, colon, backslash, name components separated by backslashes, and a terminating null character. For example, the maximum path on drive D is "D:\some 256-character path string" where "" represents the invisible terminating null character for the current system codepage. 盘符、冒号、第一个反斜杠和结尾的NUL字符占去4个字符,剩下256个字符可用。 The Windows API has many functions that also have Unicode versions to permit an extended-length path for a maximum total path length of 32767 characters. To specify an extended-length path, use the "\\?\" prefix. For example, "\\?\D:\very long path". The maximum path of 32767 characters is approximate, because the "\\?\" prefix may be expanded to a longer string by the system at run time, and this expansion applies to the total length. The "\\?\" prefix can also be used with paths constructed according to the universal naming convention (UNC). To specify such a path using UNC, use the "\\?\UNC\" prefix. For example, "\\?\UNC\server\share". These prefixes are not used as part of the path itself. They indicate that the path should be passed to the system with minimal modification, which means that you cannot use forward slashes to represent path separators, or a period to represent the current directory, or double dots to represent the parent directory. Because you cannot use the "\\?\" prefix with a relative path, relative paths are always limited to a total of MAX_PATH characters. When using an API to create a directory, the specified path cannot be so long that you cannot append an 8.3 file name (that is, the directory name cannot exceed MAX_PATH minus 12). The shell and the file system have different requirements. It is possible to create a path with the Windows API that the shell user interface is not able to interpret properly. -------------------------------------------------------------------------- "Enable Long Paths in Windows 10, Version 1607, and Later"中写有: -------------------------------------------------------------------------- Starting in Windows 10, version 1607, MAX_PATH limitations have been removed from common Win32 file and directory functions. To enable the new long path behavior, both of the following conditions must be met: The registry key HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\ LongPathsEnabled (Type: REG_DWORD) must exist and be set to 1. The key's value will be cached by the system (per process) after the first call to an affected Win32 file or directory function (see below for the list of functions). The registry key will not be reloaded during the lifetime of the process. In order for all apps on the system to recognize the value of the key, a reboot might be required because some processes may have started before the key was set. This registry key can also be controlled via Group Policy at Computer Configuration > Administrative Templates > System > Filesystem > Enable Win32 long paths. The application manifest must also include the longPathAware element. true These are the directory management functions that no longer have MAX_PATH restrictions if you opt-in to long path behavior: CreateDirectoryW CreateDirectoryExW GetCurrentDirectoryW RemoveDirectoryW SetCurrentDirectoryW These are the file management functions that no longer have MAX_PATH restrictions if you opt-in to long path behavior: CopyFileW CopyFile2 CopyFileExW CreateFileW CreateFile2 CreateHardLinkW CreateSymbolicLinkW DeleteFileW FindFirstFileW FindFirstFileExW FindNextFileW GetFileAttributesW GetFileAttributesExW SetFileAttributesW GetFullPathNameW GetLongPathNameW MoveFileW MoveFileExW MoveFileWithProgressW ReplaceFileW SearchPathW FindFirstFileNameW FindNextFileNameW FindFirstStreamW FindNextStreamW GetCompressedFileSizeW GetFinalPathNameByHandleW -------------------------------------------------------------------------- 不需要逆向工程或查看泄露的NT4、2K、XP源码,微软文档中提及的这批Win32 API就 是曾经有过MAX_PATH限制的API。 "Win32 File Namespaces"中写有: -------------------------------------------------------------------------- For file I/O, the "\\?\" prefix to a path string tells the Windows APIs to disable all string parsing and to send the string that follows it straight to the file system. For example, if the file system supports large paths and file names, you can exceed the MAX_PATH limits that are otherwise enforced by the Windows APIs. Because it turns off automatic expansion of the path string, the "\\?\" prefix also allows the use of ".." and "." in the path names, which can be useful if you are attempting to perform operations on a file with these otherwise reserved relative path specifiers as part of the fully qualified path. Many but not all file I/O APIs support "\\?\"; you should look at the reference topic for each API to be sure. -------------------------------------------------------------------------- "Win32 Device Namespaces"中写有: -------------------------------------------------------------------------- The "\\.\" prefix will access the Win32 device namespace instead of the Win32 file namespace. This is how access to physical disks and volumes is accomplished directly, without going through the file system, if the API supports this type of access. You can access many devices other than disks this way (using the CreateFile and DefineDosDevice functions, for example). For example, if you want to open the system's serial communications port 1, you can use "COM1" in the call to the CreateFile function. This works because COM1-COM9 are part of the reserved names in the NT namespace, although using the "\\.\" prefix will also work with these device names. By comparison, if you have a 100 port serial expansion board installed and want to open COM56, you cannot open it using "COM56" because there is no predefined NT namespace for COM56. You will need to open it using "\\.\COM56" because "\\.\" goes directly to the device namespace without attempting to locate a predefined alias. Another example of using the Win32 device namespace is using the CreateFile function with "\\.\PhysicalDiskX" (where X is a valid integer value) or "\\.\CdRomX". This allows you to access those devices directly, bypassing the file system. This works because these device names are created by the system as these devices are enumerated, and some drivers will also create other aliases in the system. For example, the device driver that implements the name "C:\" has its own namespace that also happens to be the file system. APIs that go through the CreateFile function generally work with the "\\.\" prefix because CreateFile is the function used to open both files and devices, depending on the parameters you use. If you're working with Windows API functions, you should use the "\\.\" prefix to access devices only and not files. Most APIs won't support "\\.\"; only those that are designed to work with the device namespace will recognize it. Always check the reference topic for each API to be sure. -------------------------------------------------------------------------- "NT Namespaces"中写有: -------------------------------------------------------------------------- There are also APIs that allow the use of the NT namespace convention, but the Windows Object Manager makes that unnecessary in most cases. To illustrate, it is useful to browse the Windows namespaces in the system object browser using the Windows Sysinternals WinObj tool. When you run this tool, what you see is the NT namespace beginning at the root, or "\". The subfolder called "Global??" is where the Win32 namespace resides. Named device objects reside in the NT namespace within the "Device" subdirectory. Here you may also find Serial0 and Serial1, the device objects representing the first two COM ports if present on your system. A device object representing a volume would be something like "HarddiskVolume1", although the numeric suffix may vary. The name "DR0" under subdirectory "Harddisk0" is an example of the device object representing a disk, and so on. To make these device objects accessible by Windows applications, the device drivers create a symbolic link (symlink) in the Win32 namespace, "Global??", to their respective device objects. For example, COM0 and COM1 under the "Global??" subdirectory are simply symlinks to Serial0 and Serial1, "C:" is a symlink to HarddiskVolume1, "Physicaldrive0" is a symlink to DR0, and so on. Without a symlink, a specified device "Xxx" will not be available to any Windows application using Win32 namespace conventions as described previously. However, a handle could be opened to that device using any APIs that support the NT namespace absolute path of the format "\Device\Xxx". With the addition of multi-user support via Terminal Services and virtual machines, it has further become necessary to virtualize the system-wide root device within the Win32 namespace. This was accomplished by adding the symlink named "GLOBALROOT" to the Win32 namespace, which you can see in the "Global??" subdirectory of the WinObj browser tool previously discussed, and can access via the path "\\?\GLOBALROOT". This prefix ensures that the path following it looks in the true root path of the system object manager and not a session-dependent path. -------------------------------------------------------------------------- ☆ 问题解决 1) subst cd c:\long_path_test\evil subst t: %cd% t: del /f /s /q *.* c: subst t: /d 2) \\?\前缀(有条件成功) del C:\long_path_test\evil\00...ee.txt 报错: 文件名或扩展名太长。 // 中文Win7 The system cannot find the path specified. // 英文Win10 1709 del \\?\C:\long_path_test\evil\00...ee.txt Win7、Win8仍然失败,Win10 1709成功删除。 2.1) Win10 1709的困惑 按微软官方文档说法,Win10 1607在此问题上开始有所改进,但要求如下注册表项就 位: -------------------------------------------------------------------------- Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem] "LongPathsEnabled"=dword:00000001 -------------------------------------------------------------------------- 我的Win10 1709该值为0,但在资源管理器中已能直接删除"00...ee.txt",与官方文 档所述有出入。 reg.exe query "HKLM\SYSTEM\CurrentControlSet\Control\FileSystem" /v "LongPathsEnabled" 若LongPathsEnabled为1,Win10中如下命令成功: del C:\long_path_test\evil\00...ee.txt 不再需要\\?\前缀。 2.2) 尾部有空格的文件名 这事与本文主旨无关,仅为备忘。John Rennie指出\\?\前缀的一种邪恶用法: echo "scz is here" > "\\?\C:\long_path_test\evil\tailspace.txt " 这将生成尾部有空格的文件名。 Win7、Win8、Win10在资源管理器中无法打开、删除、重命名"tailspace.txt "。如 果目录树中包含这种畸型文件,整个目录树都无法删干净。如果想恶搞别人,可以试 试。 可以在cmd中用\\?\前缀删除这种畸型文件: del "\\?\C:\long_path_test\evil\tailspace.txt " 2.3) 关于\\?\的其他讨论 Recursively Deleting a directory with long filename support - Larry Osterman [2015-11-16] https://docs.microsoft.com/en-us/archive/blogs/larryosterman/recursively-deleting-a-directorywith-long-filename-support 此处提到 On an NTFS filesystem, the . and .. directories don't actually exist. Instead they're pseudo directories inserted into the results of the FindFirstFile/FindNextFile API. Normally the fact that these pseudo directories don't exist isn't a problem, since the OS canonicalizes the filename and strips off the . and .. paths before it passes it onto the underlying API. But if you use the long filename prefix \\?\ the OS assumes that all filenames are canonical. And on an NTFS filesystem, there is no directory named ., so the API call such as GetFileAttributes fails! 3) powershell(失败) powershell -c "Move-Item -Path 00...ee.txt -Destination evil.txt" Win7、Win10均报错: Move-Item : Cannot find path 'C:\long_path_test\evil\00...ee.txt' because it does not exist. powershell -c "Rename-Item -Path 00...ee.txt -NewName evil.txt" Win7、Win10均报错: Rename-Item : Cannot rename because item at '00...ee.txt' does not exist. powershell -c "Remove-Item 00...ee.txt -Recurse -Force" Win7、Win10均报错: Remove-Item : 指定的路径或文件名太长,或者两者都太长。完全限定文件名必须少于260个字符,并且目录名必须少于248个字符 Remove-Item : Cannot find path 'C:\long_path_test\evil\00...ee.txt' because it does not exist. powershell方案可能是某些人YY出来的,所受限制同cmd中的del、rename等。或许有 增强版的powershell? 4) 第三方工具或自编程(未测试) 包括但不限于: Total Commander Long Path Tool 当时某人Win8中没有TC,我下了个LPT,结果还要付费注册激活全功能啥的,未进一 步测试。说不定360的那堆工具也能干这事。 一般你突然遭遇这种事的时候,不太可能有机会自编程,你只想着用系统自带工具赶 紧解决问题,然后走人。 ☆ 网友反馈 0) 陆麟的评论 MAX_PATH是shell的限制,是shell对VFAT和NTFS等文件系统折衷的兼容。cmd是shell 的一种,能突破shell限制的应用要自己解决突破限制后的后遗症。 1) 利用8.3文件名删除畸型文件 疯割、ZYH等多名网友反馈,可以利用8.3文件名删除long_path_test.7z产生的畸型 文件。 实测Win7、Win10确实可以在cmd中利用8.3文件名删除畸型文件。 dir /x del 000000~1.TXT "dir /x"可以看到8.3文件名,然后del删除之。 1.1) 无法利用8.3文件名重命名、复制畸型文件 rename 000000~1.TXT evil.txt 系统找不到指定的文件。 // 中文Win7 The system cannot find the path specified. // 英文Win10 1709 copy 000000~1.TXT evil.txt 系统找不到指定的路径。 // 中文Win7 The system cannot find the path specified. // 英文Win10 1709 subst方案可以del、rename、copy。 2) rd命令删除畸型目录树 TK建议试试rd命令,实测Win7、Win10确实可以用rd删除long_path_test目录。 cd \ rd /s /q long_path_test 相比explorer,cmd的rd没有MAX_PATH限制,而且可以删除尾部有空格的文件名。 3) 利用压缩软件删除畸型目录树 NayaWee、狗中吕布等多名网友反馈,可以利用压缩软件删除这种畸型目录树。 以7-Zip为例,右键选中long_path_test目录进行压缩,在GUI界面中指定 "操作完成后删除源文件",将在生成压缩包的同时删除long_path_test目录。 一般来说,本文只关心Windows自带工具及解决方案,不考虑第三方软件。压缩软件 也算第三方软件,为什么这里单独列一条呢?对于本文中的某人这个群体来说,她们 是不会出现在Windows上用git这类情况的,也不会在Windows上处理来自Linux的压缩 包。招灾引祸的源头更多是EndNote这类软件,产生的PDF文件名居然就是摘要中大段 大段的话,然后弄个压缩包,你发给我,我发给你,是为互害人生。 正如陆麟所说,能突破shell限制的应用要自己解决突破限制后的后遗症,所以单独 列一下压缩软件的解决方案。 4) robocopy ID为"青箬绿蓑寒江雨"的网友提到robocopy命令,这也是系统自带命令。 假设"00...ee.txt"含有有效内容,我们不想删除,只想重命名成短点的文件名,从 而可以在资源管理器中正常操作之。subst方案满足需求,也可以用robocopy命令间 接达成目的。robocopy的命令行参数比较讨嫌,不能指定目标文件名,只能指定目标 目录。 robocopy c:\long_path_test\evil c:\any 00...ee.txt /fat /mov 这条命令生成: c:\any\000000~1.TXT 同时删除: c:\long_path_test\evil\00...ee.txt 5) del命令删除畸型文件 k0nJ3cccc指出,Win7中直接用del删除当前目录下的畸型文件会失败,但可以用del 删畸型文件所在目录,此时目录本身不会被删除,其下的畸型文件会被删除。 cd c:\long_path_test\evil del 00...ee.txt 报错: The filename or extension is too long. cd c:\long_path_test del /s /q evil dir evil 提示: Deleted file - c:\long_path_test\evil\000000~1.TXT 畸型文件已被删除,从提示看,隐式使用了8.3文件名。下面这条命令一个意思: cd \ del /s /q long_path_test dir /s long_path_test del命令这个行为有点意思,之前尝试解决本问题时,都是del一个文件名,没想过指 定目录名。删了畸型文件之后,可以在资源管理器中正常操作各层目录。 进一步测试,del删"*.*"时可以删除畸型文件: cd c:\long_path_test\evil del /s /q *.* 如果只是删除畸型文件,不需要引入subst。