1. 路径字符

1.1 保留字符

Windows 系统下规定,以下保留字符不能出现在文件名中:

保留字符 说明
< 英文小于号
> 英文大于号
: 英文冒号
" 英文双引号
/ 英文正斜杠
\ 英文反斜杠
` `
? 英文问号
* 英文星号
ASCII NUL 数值为零的 ASCII 字符

1.2 Linux 文件名转 Windows

在 Linux 系统下,路径字符则没有 Windows 系统这么多限制,上述 Windows 的保留字符都可以出现在 Linux 系统路径中。因此,当跨平台同步文件时,会出现 Windows 系统上的文件同步到 Linux 系统没有问题,但 Linux 上文件名带有上述 Windows 系统的保留字符的文件则无法同步到 Windows 系统上。为了解决这个问题,本人采用的方法是在 Linux 系统中将带有上述 Windows 系统保留字符的文件名进行修改,将其中的保留字符替换成中文环境下的字符(大部分为全角),具体替换映射如下:

保留字符 替换字符
<
>
:
" (左)、(右)
/
\
` `
?
*
ASCII NUL 空(即去除)

具体 Python 代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import os
import os.path as osp
import shutil
import argparse


ILLEGAL_SYMBOL = ['<', '>', '/', '\\', '|', ':', '"', '*', '?']
REPLACE_SYMBOL = [['‹'], ['›'], ['/'], ['\'], ['|'], [':'], ['“', '”'], ['*'], ['?']]
SYMBOL_CYCLE = [1, 1, 1, 1, 1, 1, 2, 1, 1]


def main():
parse = argparse.ArgumentParser()
parse.add_argument("-d", "--dir", type=str, help="The path of the directory to be normalized.")
args = parse.parse_args()

assert osp.exists(args.dir)
for root, _, files in os.walk(args.dir):
for file in files:
new_file = file.replace('\x00', '') # remove ASCII NUL
for i in range(len(ILLEGAL_SYMBOL)):
count = 0
while ILLEGAL_SYMBOL[i] in new_file:
new_file = new_file.replace(ILLEGAL_SYMBOL[i], REPLACE_SYMBOL[i][count % SYMBOL_CYCLE[i]], 1)
count += 1

if new_file != file:
shutil.move(osp.join(root, file), osp.join(root, new_file))
# print(root, ": ", file, " -> ", new_file)


if __name__=='__main__':
main()

2. 路径长度

2.1 最大路径长度限制

  • Windows 10 文件系统(NTFS)一直以来都默认限制路径的最大长度 MAX_PATH = 260 个字节,本地路径的结构组成依次为驱动器号、冒号、反斜杠、用反斜杠分隔的路径名称组件以及终止 NULL 字符,比如 D:\{PATH}<NUL>。因此,实际有效可使用的最大路径 {PATH} 长度为 256

    对于 Unicode 路径,Windows 10 文件系统限制最大长度为 32767 个字节,此时需要在路径前加上 \\?\ 前缀来表示该路径为 Unicode 路径。

  • 而 Linux 文件系统(EXT4)则对单一文件名称限长为 255 个字节,而对总路径限长为 4096 个字节(与系统变量 PATH_MAX 相对应。

  • 在 AIX、HP-UX、Solaris 和 Mac 文件系统中,则是对单一文件名称限长为 255 个字节,而对总路径限长为 1024 个字节。

可以看到,在 Windows 10 默认的设置下,最长路径相比于其它系统要小得多,因此在不同系统之间传输/同步文件时(比如具有长文件名的 Git 存储库),很容易出现在 Windows 10 上无法显示的情况。

2.2 启用长路径

好在 Windows 10 从版本 1607 开始,提供了长路径行为,MAX_PATH = 260 限制也已经从常见的 Win32 文件和目录函数中删除了。但需要启用新的长路径行为才能生效,即修改注册表项 Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled(没有则手动创建)的值为 1 。或者直接使用 PowerShell 修改:

1
2
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" `
-Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force

启用长路径后,以下函数不再具有 MAX_PATH 限制:

  • 目录管理功能:CreateDirectoryWCreateDirectoryExWGetCurrentDirectoryWRemoveDirectoryWSetCurrentDirectoryW
  • 文件管理功能:CopyFileWCopyFile2CopyFileExWCreateFileWCreateFile2CreateHardLinkWCreateSymbolicLinkWDeleteFileWFindFirstFileWFindFirstFileExWFindNextFileWGetFileAttributesWGetFileAttributesExWSetFileAttributesWGetFullPathNameWGetLongPathNameWMoveFileExWMoveFileWithProgressWReplaceFileWSearchPathWFindFirstFileNameWFindNextFileNameWFindFirstStreamWFindNextStreamWGetCompressedFileSizeWGetFinalPathNameByHandleW

目前似乎还无法完全去除 MAX_PATH 的限制,不在以上列表中的功能函数仍然受限制。比如,目前在 PowerShell 中创建一个长为 260 的目录不行,但直接右键创建就可以。

附录