Python 3是否支持不覆盖目标文件的安全文件复制方式?
Python 3是否支持不覆盖目标文件的安全文件复制方式?
好问题!先给你明确说:2016年提到的shutil.copy/shutil.copy2会静默覆盖目标文件、以及那个notActuallySafeCopy函数存在竞态条件的问题,到现在依然是成立的——这两个工具函数确实没有内置参数来阻止覆盖行为,只要目标文件存在,它们就会悄无声息地把它覆盖掉。
那有没有安全的方式实现“不覆盖目标的文件复制”?当然有,而且可以分两种场景来实现:
兼容Python 3.3+的通用方案
核心思路是利用os.open的原子性检查特性:当你用O_CREAT | O_EXCL标志打开文件时,系统会原子性地完成“检查文件是否存在+不存在则创建”的操作,完全没有中间的时间窗口,从根本上避免了竞态条件。结合shutil.copyfileobj来复制内容,就能实现安全复制:
import os import shutil def safe_copy(src_path, dest_path): # 先提前检查源文件是否存在,避免后续复制出错 if not os.path.isfile(src_path): raise FileNotFoundError(f"源文件 {src_path} 不存在或不是常规文件") # 以独占模式创建目标文件:只有文件不存在时才会成功 try: # 打开目标文件,获得文件描述符 dest_fd = os.open( dest_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_BINARY ) except FileExistsError: raise FileExistsError(f"目标文件 {dest_path} 已存在,无法安全复制") from None # 用文件对象包装描述符,然后复制内容 try: with open(src_path, 'rb') as src_file, os.fdopen(dest_fd, 'wb') as dest_file: shutil.copyfileobj(src_file, dest_file) except Exception as e: # 如果复制过程出错,要清理已创建的空目标文件 os.remove(dest_path) raise RuntimeError(f"复制文件时出错: {str(e)}") from e
这个函数会在目标文件已存在时直接抛出FileExistsError,而且全程不会出现竞态条件,绝对安全。
Python 3.12+的简化方案
从Python 3.12开始,标准库新增了os.copy_file函数,并且支持COPYFILE_EXCL标志——这个标志的作用就是:如果目标文件已存在,复制操作直接失败。用它实现安全复制会更简洁:
import os def safe_copy_modern(src_path, dest_path): try: os.copy_file(src_path, dest_path, flags=os.COPYFILE_EXCL) except FileExistsError: raise FileExistsError(f"目标文件 {dest_path} 已存在,无法安全复制") from None
这个方法是系统级的原生实现,效率更高,代码也更简洁,适合用在新版本Python环境中。
总结一下:shutil.copy/copy2的覆盖问题至今没改,但我们有了更安全的替代方案,不用再担心竞态条件或者意外覆盖文件的问题啦。
备注:内容来源于stack exchange,提问作者Atomic Tripod
相关产品推荐
相关产品推荐

