众多Python文件复制功能,如果复制中断,哪些是安全的?
Among the many Python file copy functions, which ones are safe if the copy is interrupted?
见How do I copy a file in 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 之后),文件大小 不应报告为 文件系统中的 1GB,它应该:
- 报告写入最后一个字节时文件的大小(例如 400MB)
- 或被删除
最糟糕的情况是首先写入最终文件大小(内部使用 fallocate
或 ftruncate
?)。如果复制被中断,这将是一个问题:通过查看文件大小,我们会认为文件写入正确。
许多增量备份程序(我正在编写一个)使用“文件名+mtime+fsize”来检查文件是否必须被复制或者它是否已经存在(当然更好的解决方案是 SHA256 源和目标文件,但并不是每次同步都这样做,太费时了;这里离题了)。
所以我想确保在复制实际文件内容之前,“复制文件”功能不会立即存储最终文件大小(这样它可能会欺骗 fsize
比较)。
注意:我问这个问题是因为,虽然 shutil.filecopy
在 Python 3.7 及以下版本上相当直截了当,请参阅 source (which is more or less the naive method above), it seems much more complicated on Python 3.9, see source,[=67= 有许多不同的情况]、Linux、MacOS、“快速复制”技巧等
假设 destfile
在复制之前不存在,根据您对安全的定义,朴素方法是安全的。
shutil.copyfileobj()
和 shutil.copyfile()
紧随其后。
shutils.copy()
是下一个,shutils.copy2()
是最后一个。
解释:
根据应用程序请求保证一致性是文件系统的工作。如果您只将 X 字节写入文件,则文件大小将只占这 X 字节。
因此,像天真的方法一样直接进行FS操作就可以了。
现在的问题是这些高级函数对文件系统做了什么。
API 没有说明如果 python 在复制过程中崩溃会发生什么,但事实上每个人都期望这些函数的行为像 Unix cp
,即不要弄乱文件大小。
假设 CPython 的维护者不想破坏人们的期望,那么根据您的定义,所有这些函数都应该是安全的。
就是说,AFAICT 不能保证任何地方。
但是,shutil.copyfileobj()
和 shutil.copyfile()
明确承诺 API 不复制元数据,因此他们不太可能尝试设置大小。
shutils.copy()
不会尝试设置文件大小,只有模式,并且在大多数文件系统中设置大小和模式需要两个不同的文件系统操作,所以它应该仍然是安全的。
shutils.copy2()
说它会复制元数据,如果你看它 source code, you'll see that it only copies the metadata after copying the data, so even that should be safe. Even more, copying the metadata 不会复制大小。
所以这只会是一个问题,如果一些内部功能 python 尝试使用 ftruncate()
、fallocate()
或类似的东西进行优化,这不太可能考虑到人们编写系统 API 的人(如 python 维护者)非常了解人们的期望。
见How do I copy a file in 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 之后),文件大小 不应报告为 文件系统中的 1GB,它应该:
- 报告写入最后一个字节时文件的大小(例如 400MB)
- 或被删除
最糟糕的情况是首先写入最终文件大小(内部使用 fallocate
或 ftruncate
?)。如果复制被中断,这将是一个问题:通过查看文件大小,我们会认为文件写入正确。
许多增量备份程序(我正在编写一个)使用“文件名+mtime+fsize”来检查文件是否必须被复制或者它是否已经存在(当然更好的解决方案是 SHA256 源和目标文件,但并不是每次同步都这样做,太费时了;这里离题了)。
所以我想确保在复制实际文件内容之前,“复制文件”功能不会立即存储最终文件大小(这样它可能会欺骗 fsize
比较)。
注意:我问这个问题是因为,虽然 shutil.filecopy
在 Python 3.7 及以下版本上相当直截了当,请参阅 source (which is more or less the naive method above), it seems much more complicated on Python 3.9, see source,[=67= 有许多不同的情况]、Linux、MacOS、“快速复制”技巧等
假设 destfile
在复制之前不存在,根据您对安全的定义,朴素方法是安全的。
shutil.copyfileobj()
和 shutil.copyfile()
紧随其后。
shutils.copy()
是下一个,shutils.copy2()
是最后一个。
解释:
根据应用程序请求保证一致性是文件系统的工作。如果您只将 X 字节写入文件,则文件大小将只占这 X 字节。
因此,像天真的方法一样直接进行FS操作就可以了。
现在的问题是这些高级函数对文件系统做了什么。
API 没有说明如果 python 在复制过程中崩溃会发生什么,但事实上每个人都期望这些函数的行为像 Unix cp
,即不要弄乱文件大小。
假设 CPython 的维护者不想破坏人们的期望,那么根据您的定义,所有这些函数都应该是安全的。
就是说,AFAICT 不能保证任何地方。
但是,shutil.copyfileobj()
和 shutil.copyfile()
明确承诺 API 不复制元数据,因此他们不太可能尝试设置大小。
shutils.copy()
不会尝试设置文件大小,只有模式,并且在大多数文件系统中设置大小和模式需要两个不同的文件系统操作,所以它应该仍然是安全的。
shutils.copy2()
说它会复制元数据,如果你看它 source code, you'll see that it only copies the metadata after copying the data, so even that should be safe. Even more, copying the metadata 不会复制大小。
所以这只会是一个问题,如果一些内部功能 python 尝试使用 ftruncate()
、fallocate()
或类似的东西进行优化,这不太可能考虑到人们编写系统 API 的人(如 python 维护者)非常了解人们的期望。