【问题标题】:shutil.move deletes the file on windows when new name contains a colon (and an extension)当新名称包含冒号(和扩展名)时,shutil.move 删除 Windows 上的文件
【发布时间】:2016-03-09 22:51:21
【问题描述】:

试试:

import os, shutil

wd = os.path.abspath(os.path.curdir)
newfile = os.path.join(wd, 'testfile')
print str(newfile)
with open(newfile, 'w') as f: f.write('Hello bugs')
shutil.move(newfile, os.path.join(wd, 'testfile:.txt')) # note the :

现在检查目录 - 新文件被删除并且没有其他文件被创建 - 进程以退出代码 0 结束。

如果你发出:

shutil.move(newfile, os.path.join(wd, 'testfile:')) # note no extension

它吹了:

Traceback (most recent call last):
      File "C:/Users/MrD/.PyCharm40/config/scratches/scratch_3", line 9, in <module>
        shutil.move(newfile, os.path.join(wd, 'testfile:'))
      File "C:\_\Python27\lib\shutil.py", line 302, in move
        copy2(src, real_dst)
      File "C:\_\Python27\lib\shutil.py", line 130, in copy2
        copyfile(src, dst)
      File "C:\_\Python27\lib\shutil.py", line 83, in copyfile
        with open(dst, 'wb') as fdst:
    IOError: [Errno 22] invalid mode ('wb') or filename: 'C:\\Users\\MrD\\.PyCharm40\\config\\scratches\\testfile:'

应该如此。

这是一个错误吗?

上下文:我正在测试我的代码在给出非法文件名时的行为(: 在 Windows 文件名中是非法的),令我惊讶的是,我的程序删除了原始文件(糟糕!)并创建了一个大小为零的文件原始属性(是的,在我的情况下,文件 已创建,只是空的)和文件名 放弃给: 的文件名 - 所以像textfile:.jpg 这样的文件名给了我一个零字节@987654330 @。花了很多时间调试 - 这是 Python27\lib\shutil.py copyfile() 中的小动物(上面吹但没有吹的那条线):

我不知道为什么在我的情况下文件是在运行脚本时创建的。

【问题讨论】:

  • @PadraicCunningham:它是开放的,应该会爆炸并且不会在源代码中仔细查看 - 在回溯中可以看到开放会爆炸,但在这里它不会。 open 当然是特定于操作系统的。因为这个原因,shutil.move 恰好表现得非常糟糕 - 就像取消链接文件而不是吹一样。我可能应该编辑这个问题,但现在我需要休息一下 - 相信我这个问题的来源相当复杂。

标签: python python-2.7 shutil


【解决方案1】:

这不是 Python 的 shutilos 模块中的错误,这只是 Windows 中的怪异。 Peter Wood 在 cmets 中的link 讨论了“高级数据流”——一种 Windows 文件系统机制,它将包含元数据的隐藏文件附加到常规的可见文件中。有一个关键词附加;如果隐藏文件附加的文件被删除,则隐藏文件被删除。

似乎使用冒号将常规文件的路径与隐藏文件分开。例如,如果你在命令行中写:

> notepad foo

然后关闭记事本,开始写

> notepad foo.txt:bar

记事本将打开隐藏文件。继续在其中写一些东西,保存并关闭。键入&gt; dir 和命令行将只显示foo.txt,而不是foo.txt:bar.txt。不过果然,如果你写

> notepad foo.txt:bar.txt

您刚刚编辑的文件将出现,您的更改将保持不变。

那么,您的 Python 代码发生了什么? shutil.move 的文档说:

src 被复制(使用shutil.copy2())到dst,然后被删除。

所以当您将testfile 移动到testfile:.txt 时,Python 首先将testfile 复制到隐藏 testfile:.txt。但随后它删除 testfile,并通过这样做删除隐藏的testfile:.txt。因此,在您看来,原始文件已被删除,并且没有创建新文件。

下面的 sn-p 代码可能会让这更清楚(我已将其保存为 demo.py,并且我正在同一个空目录中运行它):

import os, shutil


with open('test', 'w') as f:
    f.write('Hello bugs')

shutil.copy2('test', 'test:foo.txt')

with open('test:foo.txt') as f:
    print(f.read())

print 'test: exists? ', os.path.exists('test')
print 'test:foo.txt exists? ', os.path.exists('test:foo.txt')
print os.listdir('.')

print('removing...')
os.remove('test')

print 'test: exists? ', os.path.exists('test')
print 'test:foo.txt exists? ', os.path.exists('test:foo.txt')
print os.listdir('.')

打印出来:

Hello bugs
test exists? True
test:foo.txt exists? True
['demo.py', 'test']
removing...
test: exists? False
test:foo.txt exists? False
['demo.py']

这表明我们可以创建一个普通文件,写入它,然后将该普通文件复制到它的隐藏流中,打开并读取它就可以了,结果与预期一致。然后我们看到os.path.exists 显示test 和它的隐藏附件test:foo.txt 都存在,即使os.listdir 只显示test。然后我们删除test,我们看到test:foo.txt 也不再存在。

最后,您无法创建没有名称的隐藏数据流,因此test: 是无效路径。在这种情况下,Python 正确地引发了异常。

因此,Python 代码实际上在 Windows 下正常运行——“备用数据流”只是一个鲜为人知的“功能”,以至于这种行为令人惊讶。

【讨论】:

  • 哇——我看到了这个链接,但实在是太困惑了,无法真正看到它在这里是如何应用的。这也解释了为什么在我的情况下创建的文件大小为 0(我移动的文件的名称不同,因此取消链接不会删除新文件及其流)。谢谢
猜你喜欢
  • 2016-12-06
  • 1970-01-01
  • 1970-01-01
  • 2017-07-16
  • 1970-01-01
  • 2011-09-27
  • 2011-12-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多