【问题标题】:Python Windows cannot stat files with invalid charactersPython Windows 无法统计具有无效字符的文件
【发布时间】:2016-12-24 10:11:02
【问题描述】:

我正在尝试制作一个快速的 Python 脚本来重命名一堆文件。这些文件是在这个 NTFS 驱动器上的 Linux 系统中制作的,但我现在在 Windows 上。命名约定如下所示:

Screenshot at 2016-12-11 21:12:56.png

: 字符在 Windows 文件名中是非法的,所以这个脚本的行为对我来说有点奇怪。

for i in os.listdir("."):
    print(i)
    x = i.replace(":", "-")
    comm = """mv "{}" "{}" """.format(i, x)
    os.system(comm)

在上面的代码中,print(i) 愉快地打印了文件名。但是,当我尝试运行 os.system(comm) 来重命名我的文件时,我收到了这个错误:

mv: cannot stat ‘Screenshot at 2016-12-24 14:54:57.png’: No such file or directory

首先,我觉得有点奇怪的是,Windows 下的 Python 可以分辨出这些顽皮的文件存在,但实际上无法移动它们。其次,解决这个问题的最佳方法是什么?

我也尝试过shutil.move()os.rename(),但没有成功。 This SO question 似乎讨论了这个问题,但似乎更关心预防而不是修复它。我显然可以切换回 Linux 并修复它,但我想知道我是否不能在 Windows 上修复它。

【问题讨论】:

  • 您将"""" 混合使用。那样有用吗?应该不是'mv "{}" "{}"'.format(i, x)(这里不需要多行)?
  • 我会使用os.rename() 而不是系统调用。
  • 所以你使用的mv,是Windows上的MSYS/Cygwin风格吗?
  • @hiroprotagonist 是的,它有效,毫无疑问你的也一样,我只是更喜欢多线。 @Jean os.rename() 也不起作用,mv 是 Windows 10 的 Linux 子系统中提供的 mv。官方称为 Bash on Ubuntu on Windows(微软一如既往地保持简洁)。
  • @Daniel,WSL 的mv 命令应该需要bash -c "mv ...",并且您必须使用/mnt 文件夹。运行 where mv 以查看 mv 正在执行什么。

标签: python linux windows


【解决方案1】:

您可以找到它们,因为它们在目录中。您无法访问它们,因为冒号符号在路径中的解析方式不同。这意味着这些文件无法通过包括MoveFile 在内的常见路径函数访问。您基本上有两个选择:查找不依赖名称的方法,例如OpenFileById,或查找文件的备用名称,例如dir /x。后者为您提供短名称 (8.3),其中不应包含任何冒号。我不知道是否有现成的函数可以从 Python 访问这些名称,因此(对我而言)最短的明确解决方法是执行 dir /x 并解析其输出。

我认为paths relative to directory descriptors 与 Python 的标准库最接近第一种方法一样,但我不知道这是否足够。底层的 FindFirstFile/FindNextFile 函数确实会在 WIN32_FIND_DATA 中生成两个名称(cFileName 和 cAlternateFileName),但 Python 期望第一个是有效的。这两种方法在 PowerShell 中也有意义,但看起来它完全不知道短名称,并且还按名称而不是 ID 跟踪文件。否则FileInfo.MoveTo 会巧妙地做到这一点。

为了首先防止这种情况,ntfs-3g 支持windows_names 选项。这会导致它在尝试创建文件时犹豫不决。

结论:正如https://superuser.com/questions/31587/how-to-force-windows-to-rename-a-file-with-a-special-character 中所讨论的,没有明确的解决方案。我所有尝试过的方法(以及少数其他方法)都在那里讨论过。可能最不混乱的选择是再次在 Linux 中挂载磁盘并从那里重命名;由于字符无效,文件系统在技术上已损坏,但微软的修复解决方案是删除,而不是重命名。

Cygwin 只是通过使用私有 unicode 字符 (':'+0xf000) 来模拟冒号。

【讨论】:

  • Linux 中的 NTFS 文件系统驱动程序是否默认创建 8.3 DOS 名称?即使在 Windows 中,也可以按卷或一般情况下禁用创建短名称。如果您没有短名称,不幸的是,通过文件 ID 打开将不起作用。 NTFS 支持文件硬链接,因此它会立即通过 ID 重命名或删除文件的尝试失败,因为目标名称不明确。该文件必须按名称打开。
  • 实际上,不太可能用 Linux 的 ntfs 驱动程序编写任何东西;使用 ntfs-3g 更为常见。它的文档提到了一个选项windows_names 来禁止这样的文件名,并且它们应该在使用 Posix 层的 Windows 上有效。确实,它们看起来可以在 Cygwin 中使用,这应该是一个更清洁的解决方案。
  • 我尝试在挂载 NTFS 卷时使用windows_names 选项,但它没有自动创建 DOS 8.3 名称。我必须通过setfattr -h -v "DOS_NAME.EXT" -n system.ntfs_dos_name "Windows long filename.ext"手动将短名称添加到文件记录中。
  • 另外,Windows 中的 NTFS 文件系统驱动程序绝对不支持包含 5 个通配符 (*?") 中的任何一个的文件名。它们保留用于NtfsQueryDirectoryFile 中的通配符匹配(系统列出目录的调用)。如果任何名称包含通配符,则调用失败并显示STATUS_FILE_CORRUPT_ERROR (0xC0000102)。可以列出包含控制字符、斜杠、管道和冒号的名称,但不能按名称打开此类文件,甚至 /mnt 下的新 Linux 子系统也没有——当然也不是 Cygwin,它只使用标准 NT 系统调用。
  • 也许 Cygwin 在 NTFS 命名流中走私无效文件名,或者其他一些巧妙的解决方法。新 Linux 子系统中的根文件系统 (VolFs) 通过转义无效字符来走私它们,例如"test*?":\|.txt" 存储为 "test#002A#003F#003C#003E#0022#003A#005C#007C.txt"。
猜你喜欢
  • 2023-04-08
  • 1970-01-01
  • 1970-01-01
  • 2018-10-21
  • 1970-01-01
  • 1970-01-01
  • 2011-08-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多