功能概述
copy_files_by_extension 是一个Python函数,用于根据文件扩展名从源目录复制文件到目标目录,同时保持原有的目录结构,并支持排除指定的目录。
主要特性
按扩展名过滤: 只复制指定扩展名的文件
保持目录结构: 在目标目录中保持与源目录相同的相对路径
排除目录支持: 可配置需要排除的目录列表
错误处理: 提供详细的复制状态和错误信息
统计信息: 输出复制结果统计和被排除的目录
函数参数说明
def copy_files_by_extension(source_dir, target_dir, extensions, exclude_dirs=None):
| 参数 | 类型 | 必需 | 描述 |
|---|---|---|---|
source_dir | str | 是 | 源目录路径 |
target_dir | str | 是 | 目标目录路径 |
extensions | list | 是 | 文件扩展名列表,如 ['.py', '.txt', '.jpg'] |
exclude_dirs | list | 否 | 要排除的目录名列表,默认为空列表 |
技术实现细节
1. 目录遍历与排除机制
使用
os.walk()递归遍历源目录通过原地修改
dirs列表实现目录排除支持大小写不敏感的扩展名匹配
2. 文件复制流程
# 核心复制逻辑 1. 检查文件扩展名是否匹配 2. 计算相对路径 3. 创建目标目录(如不存在) 4. 使用 shutil.copy2() 复制文件(保留元数据) 5. 记录复制状态
3. 错误处理
捕获并显示文件复制过程中的异常
继续处理其他文件,不因单个文件失败而中断
使用示例
基本用法
copy_files_by_extension( source_dir=r"E:Project", target_dir=r"G:Backup", extensions=['.py', '.txt', '.jpg'], exclude_dirs=['temp', '.git', '__pycache__'] )
支持的扩展名示例
代码文件:
.py,.js,.html,.css数据文件:
.json,.csv,.txt,.md图片文件:
.jpg,.png,.gif
常见排除目录
版本控制:
.git,.svn缓存目录:
__pycache__,.cache环境目录:
.venv,node_modules临时目录:
temp,logs
输出信息
复制过程输出
✓ 已复制: src/main.py ✓ 已复制: docs/readme.md ✗ 复制失败: data/temp.log - 错误: [Errno 13] Permission denied
统计信息输出
================================================== 复制完成! 共复制 156 个文件 排除的目录: 3 个 - .git - __pycache__ - temp
依赖项
Python 3.x
标准库:
osshutil
注意事项
权限要求: 需要源目录的读取权限和目标目录的写入权限
路径格式: 建议使用原始字符串(r"path")避免转义问题
磁盘空间: 确保目标目录有足够的存储空间
文件覆盖: 如果目标文件已存在,会被静默覆盖
应用场景
项目备份(排除版本控制和缓存文件)
代码迁移(只复制源代码文件)
资源文件整理(按类型分类复制)
自动化部署预处理
扩展建议
可根据需要添加以下功能:
文件大小过滤
修改时间过滤
复制进度显示
配置文件支持
日志记录功能
代码:
import os
import shutil
def copy_files_by_extension(source_dir, target_dir, extensions, exclude_dirs=None):
"""
复制指定目录下指定后缀的文件到新目录,保持目录结构,支持排除目录
Args:
source_dir: 源目录路径
target_dir: 目标目录路径
extensions: 文件后缀列表,如 ['.txt', '.py', '.jpg']
exclude_dirs: 要排除的目录名列表,如 ['temp', 'cache', '.git']
"""
if exclude_dirs is None:
exclude_dirs = []
# 确保目标目录存在
os.makedirs(target_dir, exist_ok=True)
copied_count = 0
skipped_dirs = set()
# 遍历源目录
for root, dirs, files in os.walk(source_dir):
# 在遍历过程中修改 dirs 列表来排除目录(原地修改)
dirs[:] = [d for d in dirs if d not in exclude_dirs]
# 检查当前目录是否应该被排除
current_dir = os.path.basename(root)
if current_dir in exclude_dirs:
skipped_dirs.add(root)
continue
for file in files:
# 检查文件后缀
if any(file.lower().endswith(ext.lower()) for ext in extensions):
# 源文件完整路径
source_file = os.path.join(root, file)
# 计算目标文件路径(保持相对路径)
relative_path = os.path.relpath(root, source_dir)
target_subdir = os.path.join(target_dir, relative_path)
target_file = os.path.join(target_subdir, file)
# 确保目标子目录存在
os.makedirs(target_subdir, exist_ok=True)
try:
# 复制文件(保留元数据)
shutil.copy2(source_file, target_file)
copied_count += 1
print(f"✓ 已复制: {os.path.relpath(source_file, source_dir)}")
except Exception as e:
print(f"✗ 复制失败: {os.path.relpath(source_file, source_dir)} - 错误: {e}")
# 输出统计信息
print(f"
{'=' * 50}")
print(f"复制完成!")
print(f"共复制 {copied_count} 个文件")
if skipped_dirs:
print(f"排除的目录: {len(skipped_dirs)} 个")
for dir_path in sorted(skipped_dirs):
print(f" - {os.path.relpath(dir_path, source_dir)}")
# 使用示例
if __name__ == "__main__":
source_directory = r"E:PythonProject" # 源目录
target_directory = r"g:PythonProject-backup" # 目标目录
file_extensions = ['.py', '.html', '.css', '.js', '.md', '.txt', '.jpg', '.png', '.csv', '.json'] # 要复制的文件后缀
exclude_directories = ['temp', 'cache', 'logs', '.git', '__pycache__', '.idea', '.venv'] # 要排除的目录
copy_files_by_extension(
source_directory,
target_directory,
file_extensions,
exclude_directories
)