以土豆之名,行学习之实

文件复制工具


功能概述

copy_files_by_extension 是一个Python函数,用于根据文件扩展名从源目录复制文件到目标目录,同时保持原有的目录结构,并支持排除指定的目录。

主要特性

  • 按扩展名过滤: 只复制指定扩展名的文件

  • 保持目录结构: 在目标目录中保持与源目录相同的相对路径

  • 排除目录支持: 可配置需要排除的目录列表

  • 错误处理: 提供详细的复制状态和错误信息

  • 统计信息: 输出复制结果统计和被排除的目录

函数参数说明

def copy_files_by_extension(source_dir, target_dir, extensions, exclude_dirs=None):
参数类型必需描述
source_dirstr源目录路径
target_dirstr目标目录路径
extensionslist文件扩展名列表,如 ['.py', '.txt', '.jpg']
exclude_dirslist要排除的目录名列表,默认为空列表

技术实现细节

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

  • 环境目录: .venvnode_modules

  • 临时目录: templogs

输出信息

复制过程输出

✓ 已复制: src/main.py
✓ 已复制: docs/readme.md
✗ 复制失败: data/temp.log - 错误: [Errno 13] Permission denied

统计信息输出

==================================================
复制完成!
共复制 156 个文件
排除的目录: 3 个
  - .git
  - __pycache__
  - temp

依赖项

  • Python 3.x

  • 标准库:

    • os

    • shutil

注意事项

  1. 权限要求: 需要源目录的读取权限和目标目录的写入权限

  2. 路径格式: 建议使用原始字符串(r"path")避免转义问题

  3. 磁盘空间: 确保目标目录有足够的存储空间

  4. 文件覆盖: 如果目标文件已存在,会被静默覆盖

应用场景

  • 项目备份(排除版本控制和缓存文件)

  • 代码迁移(只复制源代码文件)

  • 资源文件整理(按类型分类复制)

  • 自动化部署预处理

扩展建议

可根据需要添加以下功能:

  • 文件大小过滤

  • 修改时间过滤

  • 复制进度显示

  • 配置文件支持

  • 日志记录功能


代码:

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
    )