如如何在Python中复制文件?所示,有很多文件复制功能:
shutil.copy
shutil.copy2
shutil.copyfile
(还有shutil.copyfileobj
)
甚至是一个幼稚的方法:
with open('sourcefile', 'rb') as f, open('destfile', 'wb') as g:
while True:
block = f.read(16*1024*1024) # work by blocks of 16 MB
if not block: # EOF
break
g.write(block)
在所有这些方法中,哪些方法在复制中断的情况下是安全的(例如:杀死Python进程)?列表中的最后一个看起来不错。
我所说的安全是指:如果 1 GB 文件复制未 100% 完成(假设它在复制过程中中断,在 400MB 之后),则文件大小 不应该在文件系统中报告为 1 GB,而应该:
最糟糕的是最终文件大小首先被写入(内部带有
fallocate
或 ftruncate
?)。如果复制被中断,这将是一个问题:通过查看文件大小,我们会认为文件已正确写入。
许多增量备份程序(我正在编写一个)使用“文件名+mtime+fsize”来检查文件是否必须复制或者是否已经存在(当然更好的解决方案是SHA256源文件和目标文件,但这是不是每次同步都完成,这里太费时间了)。
所以我想确保“复制文件”功能在复制实际文件内容之前不会立即存储最终文件大小(然后它可能会欺骗
fsize
比较)。
注意:我问这个问题是因为,虽然
shutil.filecopy
在 Python 3.7 及更低版本上相当简单,请参阅 source (这或多或少是上面的简单方法),但在 Python 3.9 上似乎要复杂得多,请参阅来源,有许多不同的 Windows、Linux、MacOS 案例、“快速复制”技巧等。
假设
destfile
在复制之前不存在,则根据您对安全的定义,简单方法是安全的。
shutil.copyfileobj()
和 shutil.copyfile()
紧随其后排名第二。
接下来是 shutils.copy()
,最后一个是 shutils.copy2()
。
说明:
文件系统的工作是根据应用程序请求保证一致性。如果您仅将 X 字节写入文件,则文件大小将仅占这 X 字节。
因此,像naive方法那样直接进行FS操作就可以了。
现在的问题是这些高级函数如何处理文件系统。
API 没有说明如果 python 在复制过程中崩溃会发生什么,但事实上每个人都期望这些函数的行为像 Unix
cp
,即不要弄乱文件大小。
假设 CPython 的维护者不想打破人们的期望,那么根据您的定义,所有这些函数都应该是安全的。
也就是说,AFAICT 在任何地方都不能保证。
但是,
shutil.copyfileobj()
和 shutil.copyfile()
明确要求其 API 承诺不复制元数据,因此他们不太可能尝试设置大小。
shutils.copy()
不会尝试设置文件大小,仅设置模式,并且在大多数文件系统中设置大小和模式需要两种不同的文件系统操作,因此它应该仍然是安全的。
shutils.copy2()
表示它将复制元数据,如果您查看其源代码,您会发现它仅在复制数据后复制元数据,因此即使这样也应该是安全的。更重要的是,复制元数据不会复制大小。
因此,只有当 python 使用的某些内部函数尝试使用 ftruncate()
、
fallocate()
或类似的函数进行优化时,这才会成为问题,考虑到编写系统 API 的人(如 python 维护者)非常了解人们的期望。