`shutil.copytree(source_dir, destination_dir)` 可以将整个目录及其内容复制到新位置[ref_4]。
```python
import shutil
import os
# 定义源文件夹和目标路径
source_folder = "/path/to/your/source_folder"
destination_path = "/path/to/your/destination"
# 复制整个文件夹
shutil.copytree(source_folder, os.path.join(destination_path, "copied_folder"))
```
### **一、 核心方法对比**
| 方法 | 函数 | 功能描述 | 是否递归复制子文件夹 | 典型应用场景 |
| :--- | :--- | :--- | :--- | :--- |
| **复制整个文件夹** | `shutil.copytree(src, dst)` | 将源文件夹(src)及其所有内容(文件和子文件夹)递归地复制到目标路径(dst)。[ref_4] | **是** | 备份目录、迁移项目文件、创建副本。 |
| **复制单个文件** | `shutil.copy(src, dst)` | 将源文件(src)复制到目标文件或文件夹(dst)。如果dst是目录,则文件将复制到该目录中并保持原名。[ref_2] | **否** | 复制特定文件,例如日志或配置文件。 |
| **复制单个文件(内容)** | `shutil.copyfile(src, dst)` | 将源文件内容复制到目标文件路径。**dst必须是文件路径,不能是目录**,否则会报`IsADirectoryError`错误[ref_2]。 | **否** | 需要精确控制目标文件名的复制操作。 |
### **二、 完整代码示例与详细解释**
#### **1. 基础文件夹复制**
使用`shutil.copytree()`是最直接的方式。
```python
import shutil
import os
def copy_entire_folder(source, destination):
"""
将源文件夹完整复制到目标路径。
参数:
source (str): 要复制的源文件夹路径。
destination (str): 目标父目录路径。
"""
# 确保目标父目录存在
if not os.path.exists(destination):
print(f"目标路径不存在,正在创建: {destination}")
os.makedirs(destination)
# 构建完整的目标文件夹路径(通常保持原文件夹名)
dest_dir = os.path.join(destination, os.path.basename(source))
# 执行复制
try:
# shutil.copytree会递归复制所有内容到dest_dir[ref_4]
shutil.copytree(source, dest_dir)
print(f"成功复制文件夹 '{source}' 到 '{dest_dir}'")
except FileExistsError:
print(f"错误:目标文件夹 '{dest_dir}' 已存在。")
except Exception as e:
print(f"复制过程中发生错误: {e}")
# 使用示例
if __name__ == "__main__":
src = "./my_project_backup" # 源文件夹
dst = "./backup_archive" # 目标父文件夹
copy_entire_folder(src, dst)
```
#### **2. 选择性复制文件夹中的文件**
有时需要复制文件夹中**符合特定条件**的文件,而非整个文件夹。这通常需要结合`os.walk()`遍历和`shutil.copy()`。例如,复制所有`.jpg`图片文件[ref_3][ref_1]或文件名包含特定字符串的文件[ref_6]。
```python
import shutil
import os
import re
def copy_files_with_filter(src_dir, dst_dir, filter_func):
"""
从源目录(及其子目录)中筛选文件并复制到目标目录。
目标目录结构将被扁平化(所有文件在同一级目录)。
参数:
src_dir (str): 源根目录。
dst_dir (str): 目标目录。
filter_func (function): 过滤函数,接受文件名作为参数,返回True则复制。
"""
if not os.path.exists(dst_dir):
os.makedirs(dst_dir)
copied_count = 0
# 递归遍历源目录[ref_4]
for root, dirs, files in os.walk(src_dir):
for file in files:
if filter_func(file):
src_file_path = os.path.join(root, file)
dst_file_path = os.path.join(dst_dir, file)
# 处理目标文件名冲突
base, extension = os.path.splitext(file)
counter = 1
while os.path.exists(dst_file_path):
new_name = f"{base}_{counter}{extension}"
dst_file_path = os.path.join(dst_dir, new_name)
counter += 1
# 执行复制[ref_2]
shutil.copy(src_file_path, dst_file_path)
copied_count += 1
print(f"已复制: {src_file_path} -> {dst_file_path}")
print(f"复制完成。总共复制了 {copied_count} 个文件。")
# 使用示例1:复制所有后缀为.jpg和.png的图片文件[ref_1][ref_6]
def is_image(filename):
return filename.lower().endswith(('.jpg', '.jpeg', '.png'))
# 使用示例2:复制文件名包含特定字符串(如“_10”)的文件[ref_3]
def contains_specific_string(filename, pattern="_10"):
return pattern in filename
# 使用示例3:复制文件名符合正则表达式的文件(如report_2023*.xlsx)
def matches_regex(filename):
pattern = re.compile(r'^report_\d{4}.*\.xlsx$') # 示例:report_2023开头,.xlsx结尾
return bool(pattern.match(filename))
if __name__ == "__main__":
# 示例:复制所有图片
copy_files_with_filter("./source_data", "./extracted_images", is_image)
# 示例:复制所有包含“_10”的文件(如从子文件夹提取所有_10.jpg)[ref_3]
# copy_files_with_filter("./parent_folder", "./target_folder", lambda f: contains_specific_string(f, "_10"))
```
### **三、 进阶应用与场景**
#### **1. 保持源目录结构的复制**
上述示例2将文件扁平化到了同一目录。如果需要**保持原有的子目录结构**,则需要稍微调整复制逻辑。
```python
import shutil
import os
def copy_filtered_files_keep_structure(src_dir, dst_dir, extension='.txt'):
"""
复制源目录中指定类型的文件到目标目录,并保持原有的目录结构。
参数:
src_dir: 源目录
dst_dir: 目标目录
extension: 需要复制的文件后缀
"""
for root, dirs, files in os.walk(src_dir):
# 计算相对于源目录的相对路径
relative_path = os.path.relpath(root, src_dir)
# 构建目标目录中的对应路径
target_root = os.path.join(dst_dir, relative_path)
# 创建目标子目录(如果不存在)
if not os.path.exists(target_root):
os.makedirs(target_root)
for file in files:
if file.endswith(extension):
src_file = os.path.join(root, file)
dst_file = os.path.join(target_root, file)
shutil.copy(src_file, dst_file) # [ref_2]
print(f"已复制并保持结构: {src_file} -> {dst_file}")
if __name__ == "__main__":
copy_filtered_files_keep_structure("./logs", "./backup_logs", '.log')
```
#### **2. 处理复制冲突与权限**
在实际操作中,需要增加健壮性处理,例如目标文件夹已存在、文件权限不足等情况。`shutil.copytree` 提供了 `dirs_exist_ok` 参数(Python 3.8+)来处理目标目录已存在的情况。
```python
import shutil
import os
def robust_copy_tree(src, dst):
"""
健壮的文件夹复制,处理目标目录已存在的情况。
适用于Python 3.8+
"""
try:
# dirs_exist_ok=True 允许目标目录存在,将内容合并进去
shutil.copytree(src, dst, dirs_exist_ok=True)
print(f"成功合并复制文件夹 '{src}' 到 '{dst}'")
except PermissionError as e:
print(f"权限错误,无法访问文件: {e}")
except OSError as e:
print(f"操作系统错误: {e}")
# 对于Python 3.8以下的版本,可以先检查目标是否存在
def copy_tree_legacy(src, dst):
if os.path.exists(dst):
# 如果允许覆盖,可以先删除目标目录,或者使用合并逻辑
print(f"警告:目标目录 '{dst}' 已存在。")
# shutil.rmtree(dst) # 慎用:删除目录
# shutil.copytree(src, dst) # 再复制
else:
shutil.copytree(src, dst)
```
### **四、 关键要点与最佳实践**
1. **路径处理**:使用`os.path.join()`来构建路径可以确保代码在不同操作系统(Windows/macOS/Linux)上的兼容性。
2. **异常处理**:文件操作(复制、删除)必须用`try...except`块包裹,以处理常见的`FileNotFoundError`、`PermissionError`、`FileExistsError`等异常[ref_2]。
3. **性能考虑**:对于包含大量小文件的目录,`shutil.copytree()` 效率较高。如果需要处理海量文件或进行复杂过滤,可以考虑使用多线程或第三方库(如`pathlib`、`tqdm`用于进度条),但核心复制操作仍依赖于`shutil`模块[ref_1]。
4. **符号链接**:`shutil.copytree()`的默认行为取决于`symlinks`参数。默认情况下(或`symlinks=False`),它会复制链接指向的文件内容。如果设置`symlinks=True`,则会复制符号链接本身。
5. **替代方案**:对于简单的跨卷复制,`shutil.copy()`和`shutil.copy2()`(后者会尝试保留元数据)是单个文件操作的基础;而对于整个目录的复制,`shutil.copytree()`是标准且最常用的工具[ref_2][ref_4]。