问题是您不允许在 Windows 上将冒号放入文件名中。您实际上并未使用 Windows……但您使用的是 SMB 共享,这意味着您受 Windows 规则的约束。
解决方法是不要将冒号放入文件名中。
如果您想了解为什么会发生这种奇怪的事情,请继续阅读。
Windows 文件名的详细信息在 MSDN 的命名文件、路径和命名空间中进行了描述,但我将在这里总结相关部分。
Windows 下的 NT 内核对冒号没有问题,但它上面的 Win32 层无法处理它们(MSVCRT 中的 quasi-POSIX 层位于 Win32 之上)。
因此,在 C 级别,如果您调用 NT 函数,例如NtSetInformationFile
,它会很好地保存它们。如果你调用 Win32 函数如MoveFileEx
,它们通常会给你一个错误,但如果你使用特殊\\?\
语法说“将此名称直接传递给 NT”,它会起作用。如果你调用 MSVCRT 之类的函数rename
,你会得到一个错误。旧版本的 Python 称为rename
,它只会给你一个错误。较新的版本调用MoveFileEx
,并将尝试将名称包装在\\?\
语法中(因为这也允许您绕过其他一些愚蠢的限制,例如过短的MAX_PATH
值)。
那么,如果你给一个文件取了一个 Win32 无法理解的名字会怎样呢?请记住,在 Windows 上,每个文件都有两个不同的名称:“长名称”和“短名称”。短名称是 DOS 风格的 8.3 文件名。因此,当它无法显示长名称时,它会显示短名称。
简称从何而来?如果您没有显式创建一个,Windows 将使用前 6 个字符、一个波浪号和一些字母从长名称中为您创建一个。因此,例如,的简称"Program Files"
是"PROGRA~1"
。但如果 Windows 不能处理长名称,它只会用 6 个随机字符、一个波浪号和一个随机字符组成一个短名称。所以你得到类似的东西2A443K~H
。
为 Windows 设计的 NTFS 文件系统期望以 Windows-y 方式使用。因此,如果您使用的是 NTFS 卷,即使在非 Windows 系统上,驱动程序也会模拟其中的一些功能,为您提供类似但不相同的行为。
当然,如果您正在与来自 Windows 系统的共享或由非 Windows 系统上的 NTFS 驱动器支持的共享通信,同样,一些相同的事情将适用。
即使您的计算机和文件服务器都是非 Windows 并且文件系统不是 NTFS,如果您使用 SMB/CIFS 进行文件共享,SMB 也是为 Windows 设计的,您将再次获得类似的行为。
至少您不再需要担心 VMS、经典 Mac 和其他命名系统,只需 POSIX 和 Windows。