pin_drop当前位置:知识文库 ❯ 图文

Python shutil模块 - 高级文件操作与目录管理

在Python中,shutil模块是高级文件操作的核心模块。与os模块不同,shutil专注于文件内容的复制、移动、删除以及目录树的整体操作。本篇教程将详细介绍shutil模块的核心功能和实际应用场景。

一、shutil模块概述

shutil(Shell Utilities的缩写)是Python的标准库之一,提供了对文件和目录集合的高级操作。当需要复制、移动或删除包含文件的整个目录时,shutil模块是最佳选择。它比os模块更适合处理需要操作文件内容的场景。

使用shutil模块前,需要先导入它:

代码示例

import shutil

shutil模块与os模块互补使用:os模块处理路径和低级别操作,shutil模块处理高级别的文件复制、移动和归档操作。

二、常用语法与函数

shutil模块提供了丰富的高级文件操作函数,以下是常用的核心函数:

文件复制函数

  • shutil.copy(src, dst):复制文件内容,不保留元数据

  • shutil.copy2(src, dst):复制文件并保留完整元数据(修改时间等)

  • shutil.copyfile(src, dst):仅复制文件内容,dst必须是完整文件路径

  • shutil.copytree(src, dst):递归复制整个目录树

文件移动与删除函数

  • shutil.move(src, dst):移动文件或目录(跨文件系统时为复制+删除)

  • shutil.rmtree(path):递归删除整个目录树(包括非空目录)

磁盘与权限函数

  • shutil.disk_usage(path):获取磁盘使用情况(总计、已用、可用)

  • shutil.chown(path, user, group):修改文件所有者和组

  • shutil.which(cmd):在PATH中查找可执行文件的路径

归档压缩函数

  • shutil.make_archive(base_name, format, root_dir):创建归档文件(zip、tar等)

  • shutil.unpack_archive(filename, extract_dir):解压归档文件


三、基本用法详解

1. 复制文件

shutil模块提供了多种复制文件的方法,区别在于是否保留元数据以及目标路径的处理方式。

代码示例

import shutil

# copy() - 复制文件内容,目标可以是目录
shutil.copy('source.txt', 'destination.txt')
shutil.copy('source.txt', '/path/to/directory/')

# copy2() - 复制文件并保留元数据(推荐)
shutil.copy2('source.txt', 'destination.txt')

# copyfile() - 仅复制内容,目标必须是完整路径
shutil.copyfile('source.txt', 'destination.txt')

# copyfileobj() - 从文件对象复制到文件对象
with open('source.txt', 'rb') as fsrc:
    with open('destination.txt', 'wb') as fdst:
        shutil.copyfileobj(fsrc, fdst)

2. 复制目录树

shutil.copytree()可以递归复制整个目录树,包括所有子目录和文件。

代码示例

import shutil

# 复制整个目录树(目标目录不能存在)
shutil.copytree('source_dir', 'destination_dir')

# 忽略特定文件或目录
shutil.copytree('source_dir', 'destination_dir', 
                ignore=shutil.ignore_patterns('*.pyc', '__pycache__'))

# 自定义忽略函数
def ignore_pycache_and_tests(dir, files):
    return [f for f in files if f.endswith('.pyc') or f == 'tests']

shutil.copytree('source_dir', 'destination_dir', 
                ignore=ignore_pycache_and_tests)

# dirs_exist_ok=True 允许目标目录已存在(Python 3.8+)
shutil.copytree('source_dir', 'destination_dir', dirs_exist_ok=True)

3. 移动文件和目录

shutil.move()用于移动文件或目录。在同一文件系统内是重命名操作,跨文件系统时则是复制后删除原文件。

代码示例

import shutil

# 移动文件
shutil.move('old_location.txt', 'new_location.txt')

# 移动到目录
shutil.move('file.txt', '/path/to/directory/')

# 移动并重命名
shutil.move('old_name.txt', '/path/to/new_name.txt')

# 移动整个目录
shutil.move('source_dir', 'destination_dir')

4. 删除目录树

shutil.rmtree()是删除非空目录的唯一标准方法。它会递归删除目录及其所有内容。

代码示例

import shutil

# 删除整个目录树
shutil.rmtree('directory_to_delete')

# 忽略删除错误
shutil.rmtree('directory_to_delete', ignore_errors=True)

# 自定义错误处理
def error_handler(func, path, exc_info):
    print(f'删除 {path} 时出错: {exc_info}')

shutil.rmtree('directory_to_delete', onerror=error_handler)

5. 创建和解压归档文件

shutil模块内置了对常见压缩格式的支持,可以方便地创建和解压归档文件。

代码示例

import shutil

# 创建zip归档
shutil.make_archive('backup', 'zip', '/path/to/source_dir')

# 创建tar.gz归档
shutil.make_archive('backup', 'gztar', '/path/to/source_dir')

# 创建带前缀的归档
shutil.make_archive('my_project_v1', 'zip', 
                    root_dir='/path/to/project',
                    base_dir='.')

# 解压归档文件
shutil.unpack_archive('backup.zip', '/path/to/extract_dir')
shutil.unpack_archive('backup.tar.gz', '/path/to/extract_dir')

四、完整代码示例

示例1:备份目录并创建归档

代码示例

import shutil
import os
from datetime import datetime

def backup_directory(source_dir, backup_dir):
    """备份目录并创建带时间戳的zip归档"""
    # 确保备份目录存在
    os.makedirs(backup_dir, exist_ok=True)
    
    # 生成带时间戳的文件名
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
    archive_name = os.path.join(backup_dir, f'backup_{timestamp}')
    
    # 创建zip归档
    archive_path = shutil.make_archive(archive_name, 'zip', source_dir)
    
    print(f'备份完成: {archive_path}')
    return archive_path

# backup_directory('./my_project', './backups')

示例2:同步两个目录

代码示例

import shutil
import os

def sync_directories(source, destination):
    """将源目录的内容同步到目标目录"""
    os.makedirs(destination, exist_ok=True)
    
    for item in os.listdir(source):
        src_path = os.path.join(source, item)
        dst_path = os.path.join(destination, item)
        
        if os.path.isfile(src_path):
            # 如果目标文件不存在或源文件更新,则复制
            if not os.path.exists(dst_path) or \
               os.path.getmtime(src_path) > os.path.getmtime(dst_path):
                shutil.copy2(src_path, dst_path)
                print(f'已更新: {item}')
        elif os.path.isdir(src_path):
            # 递归同步子目录
            sync_directories(src_path, dst_path)

# sync_directories('./source', './destination')

示例3:清理临时文件

代码示例

import shutil
import os

def clean_temp_directory(temp_dir, max_age_days=7):
    """清理超过指定天数的临时文件"""
    import time
    
    current_time = time.time()
    max_age_seconds = max_age_days * 24 * 60 * 60
    
    for item in os.listdir(temp_dir):
        item_path = os.path.join(temp_dir, item)
        
        try:
            # 获取文件修改时间
            file_age = current_time - os.path.getmtime(item_path)
            
            if file_age > max_age_seconds:
                if os.path.isfile(item_path):
                    os.remove(item_path)
                    print(f'已删除文件: {item}')
                elif os.path.isdir(item_path):
                    shutil.rmtree(item_path)
                    print(f'已删除目录: {item}')
        except Exception as e:
            print(f'处理 {item} 时出错: {e}')

# clean_temp_directory('./temp', max_age_days=7)

示例4:批量移动文件

代码示例

import shutil
import os

def move_files_by_extension(source_dir, dest_dir, extension):
    """根据扩展名批量移动文件"""
    os.makedirs(dest_dir, exist_ok=True)
    
    moved_count = 0
    for filename in os.listdir(source_dir):
        if filename.endswith(extension):
            src = os.path.join(source_dir, filename)
            dst = os.path.join(dest_dir, filename)
            
            shutil.move(src, dst)
            moved_count += 1
            print(f'已移动: {filename}')
    
    print(f'共移动了 {moved_count} 个文件')

# move_files_by_extension('./downloads', './images', '.jpg')
# move_files_by_extension('./downloads', './documents', '.pdf')

示例5:获取磁盘使用信息

代码示例

import shutil

def display_disk_usage(path='/'):
    """显示指定路径的磁盘使用情况"""
    usage = shutil.disk_usage(path)
    
    # 转换为GB
    total_gb = usage.total / (1024 ** 3)
    used_gb = usage.used / (1024 ** 3)
    free_gb = usage.free / (1024 ** 3)
    percent_used = (usage.used / usage.total) * 100
    
    print(f'路径: {path}')
    print(f'总容量: {total_gb:.2f} GB')
    print(f'已使用: {used_gb:.2f} GB ({percent_used:.1f}%)')
    print(f'可用空间: {free_gb:.2f} GB')

# display_disk_usage('C:/')  # Windows
# display_disk_usage('/')    # Linux/macOS

示例6:安全的文件复制(带进度提示)

代码示例

import shutil
import os

def copy_with_progress(src, dst):
    """带进度提示的文件复制"""
    file_size = os.path.getsize(src)
    
    with open(src, 'rb') as fsrc:
        with open(dst, 'wb') as fdst:
            copied = 0
            while True:
                # 每次读取1MB
                chunk = fsrc.read(1024 * 1024)
                if not chunk:
                    break
                fdst.write(chunk)
                copied += len(chunk)
                
                # 显示进度
                progress = (copied / file_size) * 100
                print(f'\r进度: {progress:.1f}%', end='')
    
    print('\n复制完成!')

# copy_with_progress('large_file.zip', 'backup/large_file.zip')

五、注意事项与最佳实践

注意1shutil.copytree()默认要求目标目录不存在。在Python 3.8+中,可以使用dirs_exist_ok=True参数来允许目标目录已存在。

注意2shutil.rmtree()是不可逆的删除操作,会永久删除整个目录树。执行前务必仔细检查路径,建议先使用os.path.exists()验证路径,并考虑添加用户确认步骤。

注意3:复制大文件时,建议使用shutil.copyfileobj()并指定缓冲区大小,以便更好地控制内存使用和提供进度反馈。

注意4shutil.move()在跨文件系统移动文件时,实际上是先复制再删除源文件。如果复制过程中断,可能导致源文件和目标文件同时存在。建议在移动大文件前检查磁盘空间。

小贴士

如果需要保留文件的完整元数据(包括权限、修改时间、访问时间等),始终使用shutil.copy2()而不是shutil.copy()。copy2()在备份场景下尤为重要,因为它能确保备份文件与原始文件的时间戳一致。


六、shutil与os对比

shutil模块和os模块都涉及文件操作,但它们的侧重点不同。理解两者的区别有助于选择合适的工具:

对比项 shutil模块 os模块
主要用途 高级文件操作(复制、移动、归档) 低级文件和目录操作
复制文件 shutil.copy/copy2/copyfile 不直接支持,需手动实现
删除目录 rmtree()可删除非空目录 rmdir()仅能删除空目录
移动文件 shutil.move() os.rename()(仅限同文件系统)
归档压缩 make_archive/unpack_archive 不支持
路径操作 不直接支持 os.path提供完整路径操作
操作级别 高级(文件内容级别) 低级(系统调用级别)

七、小结

  • shutil模块专注于高级文件操作,是复制、移动和删除文件的最佳选择

  • shutil.copy2()比copy()更推荐,因为它保留了文件的完整元数据

  • shutil.rmtree()是删除非空目录的标准方法,操作不可逆需谨慎

  • shutil.copytree()支持忽略模式和dirs_exist_ok参数,灵活控制复制行为

  • shutil.make_archive()和unpack_archive()提供了便捷的归档压缩功能

  • shutil.move()支持跨文件系统移动,但大文件移动前需检查磁盘空间


八、练习题

练习1

编写一个函数sync_backup(source, backup_dir, max_backups=5),实现自动备份功能:将源目录备份到指定目录,每次备份创建带时间戳的zip文件,并自动删除超过max_backups数量的旧备份。

练习2

编写一个函数organize_downloads(download_dir),扫描下载目录中的文件,根据文件扩展名自动创建对应的子目录(如images、documents、videos等),并将文件移动到相应的子目录中。

常见问题

shutil.copy()和shutil.copy2()有什么区别?

shutil.copy()只复制文件内容和新文件的权限,不保留原始文件的元数据(如修改时间、访问时间)。shutil.copy2()会尽可能保留所有文件元数据,包括修改时间和访问时间。在备份场景下,推荐使用copy2()以确保备份文件与原始文件的时间戳一致。

shutil.move()和os.rename()有什么区别?

os.rename()只能在同一个文件系统内重命名或移动文件,跨文件系统时会失败。shutil.move()则更智能,在同一文件系统内使用rename,跨文件系统时自动执行复制+删除操作。因此,shutil.move()更加通用和可靠。

如何安全地删除一个可能包含只读文件的目录?

可以使用shutil.rmtree()的onerror参数来自定义错误处理函数。在Windows上,只读文件可能导致删除失败。可以编写一个错误处理函数,在遇到权限错误时先修改文件权限再重试删除。或者使用ignore_errors=True参数忽略所有错误(但这样可能无法完全删除目录)。

shutil支持哪些归档格式?

shutil.make_archive()支持的格式取决于系统安装的库。常见的包括:zip(需要zlib)、tar(内置)、gztar(gzip压缩的tar,需要zlib)、bztar(bzip2压缩的tar,需要bz2)、xztar(xz压缩的tar,需要lzma)。可以使用shutil.get_archive_formats()查看系统支持的所有格式。

标签: shutil模块 文件复制 文件移动 目录操作 归档压缩 Python教程 文件备份

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: Python os模块 下一篇: Python pathlib模块 - 面向对象的路径操作

poll相关推荐