使用shutil.copy/copy2复制打开文件时触发[Errno 13]权限拒绝异常
文件备份时无法复制打开状态文件的问题
问题情况
开发小型备份软件,监听文件modified和created事件,多数场景正常,但遇到以下异常:
- 创建Word文件可正常备份
- 打开Word文件修改保存后,触发权限错误:
[Errno 13] Permission denied
用到的代码片段:
if os.path.isfile(event.src_path): if os.path.exists(destinationFilePath) and event.event_type == 'modified': os.remove(destinationFilePath) #We need a short sleep, because the file may be in use by windows creation routine time.sleep(1) shutil.copy2(event.src_path, destinationFilePath) logging.info(f"File created: {destinationFilePath}")
尝试过shutil.copy、shutil.copy2以及subprocess调用系统copy命令,均出现相同权限拒绝问题。
解决方案
1. 增加重试与指数退避
Windows下文件保存后可能被进程短暂锁定,固定1秒sleep不足以覆盖所有情况,改用带重试的逻辑:
import time import shutil import logging def copy_with_retry(src, dest, max_retries=5, initial_delay=1): delay = initial_delay for attempt in range(max_retries): try: if os.path.exists(dest): os.remove(dest) shutil.copy2(src, dest) logging.info(f"Successfully copied: {dest}") return True except PermissionError: if attempt < max_retries - 1: time.sleep(delay) delay *= 2 # 指数退避,每次等待时间翻倍 else: logging.error(f"Failed to copy {src} after {max_retries} attempts") return False # 替换原代码中的复制部分 if os.path.isfile(event.src_path): copy_with_retry(event.src_path, destinationFilePath)
2. 使用Windows卷影复制服务(VSS)
如果文件被长期占用,普通复制方法无法读取,可以通过VSS创建磁盘快照,从快照中复制文件。需要安装pywin32库:
import win32com.client import shutil import os def copy_locked_file(src, dest): # 初始化VSS客户端 vss_client = win32com.client.Dispatch("VssClient.VssClient") vss_client.Initialize() try: # 创建快照集并添加目标路径的快照 snapshot_set = vss_client.CreateSnapshotSet() snapshot = snapshot_set.AddSnapshot(os.path.dirname(src)) snapshot_set.Prepare() snapshot_set.Commit() # 获取快照的设备路径,拼接成快照中的文件路径 snapshot_device = snapshot.GetSnapshotDeviceObject() snapshot_src = os.path.join(snapshot_device, os.path.basename(src)) # 从快照复制文件 if os.path.exists(dest): os.remove(dest) shutil.copy2(snapshot_src, dest) logging.info(f"Copied locked file from snapshot: {dest}") finally: # 清理VSS资源 vss_client.Cleanup()
3. 检测文件是否被锁定后再复制
使用psutil库检测文件是否被其他进程占用,等待释放后再执行复制:
import psutil import time import shutil import logging def is_file_locked(file_path): for proc in psutil.process_iter(['pid', 'name']): try: for handle in proc.open_files(): if handle.path == file_path: return True except (psutil.NoSuchProcess, psutil.AccessDenied): continue return False # 修改原代码逻辑 if os.path.isfile(event.src_path): # 等待文件解锁 while is_file_locked(event.src_path): time.sleep(1) # 执行复制 if os.path.exists(destinationFilePath) and event.event_type == 'modified': os.remove(destinationFilePath) shutil.copy2(event.src_path, destinationFilePath) logging.info(f"File created: {destinationFilePath}")
内容的提问来源于stack exchange,提问作者Daniel Müller
相关产品推荐
相关产品推荐

