Python 的 shutil.copyfile() 函数本身的设计就是“覆盖”模式,如果目标文件已经存在,它会在不提示任何警告的情况下,直接用源文件的内容替换目标文件的全部内容。

下面我将从基础用法、安全注意事项、与其他复制函数的对比以及完整示例几个方面来详细说明。
shutil.copyfile() 的基础用法
shutil.copyfile() 函数位于 shutil 模块中,用于精确地复制文件内容。
函数签名
shutil.copyfile(src, dst, *, follow_symlinks=True)
参数
src(str): 源文件的路径。dst(str): 目标文件的路径。- 关键点:
dst是一个已存在的文件,它的内容将被完全覆盖。 dst是一个目录,会抛出IsADirectoryError错误。dst不存在,函数会自动创建它。
- 关键点:
follow_symlinks(bool): 是否遵循符号链接,默认为True,如果设为False,则会创建一个指向源文件符号链接的新符号链接。
简单示例
假设我们有以下文件结构:
/my_project/
├── source.txt (内容: "这是源文件")
└── destination.txt (内容: "这是旧的目标文件")
运行以下 Python 代码:

import shutil
# 定义源文件和目标文件路径
source_file = 'source.txt'
destination_file = 'destination.txt'
# 执行复制操作
shutil.copyfile(source_file, destination_file)
print(f"文件 {source_file} 的内容已复制并覆盖了 {destination_file}。")
执行后,destination.txt 的内容会从 "这是旧的目标文件" 变为 "这是源文件",这就是“覆盖”的效果。
安全注意事项:如何防止意外覆盖
直接覆盖文件存在风险,可能会导致重要数据丢失,在实际开发中,我们通常需要一些机制来防止意外覆盖。
手动检查文件是否存在(最常用)
在执行 copyfile 之前,先检查目标文件是否存在,如果存在,你可以选择跳过、重命名目标文件或提示用户。
import shutil
import os
source_file = 'source.txt'
destination_file = 'destination.txt'
# 检查目标文件是否存在
if os.path.exists(destination_file):
print(f"警告: 目标文件 {destination_file} 已存在!")
# 在这里可以添加你的逻辑,
# 1. 跳过操作
# print("操作已取消。")
# return
# 2. 询问用户 (更友好)
# user_input = input("是否覆盖? (y/n): ")
# if user_input.lower() != 'y':
# print("操作已取消。")
# return
# 3. 给目标文件添加时间戳后缀再复制
base, ext = os.path.splitext(destination_file)
timestamp = int(os.path.getmtime(source_file))
new_destination = f"{base}_{timestamp}{ext}"
print(f"将文件复制到新路径: {new_destination}")
shutil.copyfile(source_file, new_destination)
else:
# 文件不存在,直接复制
shutil.copyfile(source_file, destination_file)
print(f"文件 {source_file} 已成功复制到 {destination_file}。")
使用 try...except 捕获错误
虽然 copyfile 不会因为文件存在而报错,但如果你想在复制过程中捕获其他可能的错误(如权限不足、磁盘空间不足等),可以使用 try...except 块。
import shutil
source_file = 'source.txt'
destination_file = 'destination.txt'
try:
shutil.copyfile(source_file, destination_file)
print(f"文件 {source_file} 已成功复制到 {destination_file}。")
except FileNotFoundError:
print(f"错误: 源文件 {source_file} 未找到!")
except PermissionError:
print(f"错误: 没有权限写入目标文件 {destination_file}。")
except Exception as e:
print(f"发生未知错误: {e}")
shutil 模块中其他复制函数的对比
shutil 模块提供了多个复制函数,理解它们的区别很重要。
| 函数名 | 描述 | 目标存在时 | 复制元数据 (如权限) | 复制目录 |
|---|---|---|---|---|
shutil.copyfile(src, dst) |
仅复制文件内容 | 直接覆盖 | 否 | 否 |
shutil.copy(src, dst) |
复制文件内容和权限 | 直接覆盖 | 是 | 否 |
shutil.copy2(src, dst) |
复制文件内容和所有元数据 (如修改时间) | 直接覆盖 | 是 | 否 |
shutil.copytree(src, dst) |
递归地复制整个目录树 | 直接覆盖 | 是 | 是 |
选择建议:
- 如果你只想复制文件内容,不关心权限和修改时间,用
shutil.copyfile()。 - 如果你想保留文件的权限(可执行文件),用
shutil.copy()。 - 如果你想尽可能完整地保留文件的元数据(包括修改时间、创建时间等),用
shutil.copy2(),这是最常用和最推荐的文件复制方法。 - 如果你要复制一个文件夹及其所有内容,用
shutil.copytree()。
完整示例:安全的文件复制函数
下面是一个封装好的函数,它结合了检查和用户交互,实现了更安全的文件复制。
import shutil
import os
def safe_copy_file(source_path, destination_path, overwrite=False):
"""
安全地复制文件,可以选择是否覆盖已存在的目标文件。
:param source_path: 源文件路径。
:param destination_path: 目标文件路径。
:param overwrite: 如果为 True,则覆盖已存在的目标文件,默认为 False。
:return: 如果复制成功返回 True,否则返回 False。
"""
# 检查源文件是否存在
if not os.path.exists(source_path):
print(f"错误: 源文件 '{source_path}' 不存在。")
return False
# 检查目标文件是否存在
if os.path.exists(destination_path):
if not overwrite:
print(f"提示: 目标文件 '{destination_path}' 已存在,未启用覆盖模式,操作已取消。")
return False
else:
print(f"警告: 目标文件 '{destination_path}' 已存在,即将被覆盖。")
try:
# 使用 copy2 来保留元数据
shutil.copy2(source_path, destination_path)
print(f"成功: 文件已从 '{source_path}' 复制到 '{destination_path}'。")
return True
except Exception as e:
print(f"错误: 复制文件时发生异常 - {e}")
return False
# --- 使用示例 ---
if __name__ == "__main__":
# 创建一个用于测试的源文件
with open("my_data.txt", "w", encoding="utf-8") as f:
f.write("这是重要的数据。")
# 第一次尝试复制 (不覆盖)
print("--- 第一次尝试 ---")
safe_copy_file("my_data.txt", "backup/my_data.txt")
# 第二次尝试复制 (不覆盖)
print("\n--- 第二次尝试 (不覆盖) ---")
safe_copy_file("my_data.txt", "backup/my_data.txt")
# 第三次尝试复制 (覆盖)
print("\n--- 第三次尝试 (覆盖) ---")
safe_copy_file("my_data.txt", "backup/my_data.txt", overwrite=True)
# 清理测试文件
if os.path.exists("my_data.txt"):
os.remove("my_data.txt")
if os.path.exists("backup/my_data.txt"):
os.remove("backup/my_data.txt")
if os.path.exists("backup") and not os.listdir("backup"):
os.rmdir("backup")
shutil.copyfile()默认就是覆盖模式,直接替换目标文件的全部内容。- 安全第一:在生产环境中,务必在复制前检查目标文件是否存在,并决定如何处理(跳过、重命名或提示用户)。
- 选择合适的工具:根据你的需求(是否需要保留权限、修改时间等)选择
copyfile、copy或copy2。copy2是更优的选择。
