Python高级技术之:`Python`的`shutil`模块:高级文件操作,如复制、移动和归档。

各位观众,大家好!我是今天的主讲人,江湖人称“代码老司机”。今天咱们聊聊 Python 里一个低调但实力超群的模块:shutil。这玩意儿就像文件操作界的瑞士军刀,能复制、移动、重命名,还能打包压缩,绝对是居家旅行、办公必备良品。

一、初识 shutil:你的文件操作小助手

简单来说,shutil 模块是 Python 标准库中的一员,专门用来处理高级的文件和目录操作。它建立在 os 模块的基础上,提供了更强大、更便捷的功能。如果你觉得 os.path 里的那些函数不够用,或者写起来太繁琐,那就该考虑 shutil 了。

二、shutil 的常用功能:十八般武艺样样精通

接下来,咱们逐一过过 shutil 的常用功能,看看它到底有多厉害。

1. 文件复制:克隆大法好!

  • shutil.copy(src, dst):简单复制

    这是最基本的复制操作,将文件 src 复制到 dst。如果 dst 是一个目录,则会在该目录下创建一个同名文件。

    import shutil
    
    src_file = 'my_file.txt'
    dst_file = 'my_file_copy.txt'
    
    # 先创建一个源文件
    with open(src_file, 'w') as f:
        f.write('Hello, shutil!')
    
    shutil.copy(src_file, dst_file)
    
    print(f"文件 '{src_file}' 已复制到 '{dst_file}'")
  • shutil.copy2(src, dst):复制文件和元数据

    copy2 不仅复制文件内容,还会尽可能地保留原始文件的元数据,比如修改时间、访问时间等。这对于需要精确保留文件信息的场景非常有用。

    import shutil
    import os
    import time
    
    src_file = 'my_file.txt'
    dst_file = 'my_file_copy2.txt'
    
    # 先修改源文件的修改时间
    modification_time = time.time() - 3600  # 一小时前
    os.utime(src_file, (modification_time, modification_time))
    
    shutil.copy2(src_file, dst_file)
    
    # 验证修改时间是否被保留
    src_mtime = os.path.getmtime(src_file)
    dst_mtime = os.path.getmtime(dst_file)
    
    print(f"源文件修改时间:{src_mtime}")
    print(f"复制文件修改时间:{dst_mtime}")
    assert abs(src_mtime - dst_mtime) < 1, "修改时间未被正确复制" # 允许1秒的误差
  • shutil.copyfile(src, dst):仅复制文件内容

    copyfile 只复制文件的内容,不关心任何元数据。注意,dst 必须是一个文件路径,不能是目录。如果 dst 文件已存在,会被覆盖。

    import shutil
    
    src_file = 'my_file.txt'
    dst_file = 'my_file_copyfile.txt'
    
    shutil.copyfile(src_file, dst_file)
    
    print(f"文件 '{src_file}' 的内容已复制到 '{dst_file}'")

2. 目录复制:一键克隆整个文件夹

  • shutil.copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, ignore_dangling_symlinks=False, dirs_exist_ok=False):复制整个目录树

    这绝对是 shutil 的王牌功能之一。它可以递归地复制整个目录树,包括所有文件和子目录。

    • symlinks: 如果为 True,复制符号链接本身,而不是链接指向的文件。默认为 False,复制链接指向的文件。
    • ignore: 一个可调用对象,用于过滤不需要复制的文件或目录。
    • copy_function: 用于复制文件的函数,默认为 copy2
    • ignore_dangling_symlinks: 如果为 True,忽略无效的符号链接。
    • dirs_exist_ok: 如果为 True,且目标目录已存在,不会抛出异常。
    import shutil
    import os
    
    src_dir = 'my_source_directory'
    dst_dir = 'my_destination_directory'
    
    # 先创建一个源目录和一些文件
    os.makedirs(src_dir, exist_ok=True)
    with open(os.path.join(src_dir, 'file1.txt'), 'w') as f:
        f.write('This is file1.')
    os.makedirs(os.path.join(src_dir, 'subdir'), exist_ok=True)
    with open(os.path.join(src_dir, 'subdir', 'file2.txt'), 'w') as f:
        f.write('This is file2.')
    
    shutil.copytree(src_dir, dst_dir)
    
    print(f"目录 '{src_dir}' 已复制到 '{dst_dir}'")

    ignore 参数的用法:

    import shutil
    import os
    
    def ignore_patterns(*patterns):
        def _ignore_patterns(path, names):
            from fnmatch import filter
            ignored_names = set()
            for pattern in patterns:
                ignored_names.update(filter(names, pattern))
            return set(os.path.join(path, name) for name in ignored_names)
        return _ignore_patterns
    
    src_dir = 'my_source_directory'
    dst_dir = 'my_destination_directory'
    
    # 忽略所有 .txt 文件
    shutil.copytree(src_dir, dst_dir, ignore=ignore_patterns('*.txt'))
    
    print(f"目录 '{src_dir}' 已复制到 '{dst_dir}',忽略了所有 .txt 文件")

3. 文件和目录移动:乾坤大挪移!

  • shutil.move(src, dst, copy_function=copy2):移动文件或目录

    move 函数可以将文件或目录从 src 移动到 dst。如果 dst 是一个目录,则 src 会被移动到该目录下。如果 dst 文件已存在,则会被覆盖。如果源和目标在同一文件系统上, move 会简单地重命名 src。否则,src 将被复制到 dst,然后 src 被删除。

    import shutil
    import os
    
    src_file = 'my_file.txt'
    dst_file = 'moved_file.txt'
    
    # 先创建一个源文件
    with open(src_file, 'w') as f:
        f.write('Hello, move!')
    
    shutil.move(src_file, dst_file)
    
    print(f"文件 '{src_file}' 已移动到 '{dst_file}'")

    移动目录的例子:

    import shutil
    import os
    
    src_dir = 'my_source_directory'
    dst_dir = 'moved_directory'
    
    # 先创建一个源目录
    os.makedirs(src_dir, exist_ok=True)
    
    shutil.move(src_dir, dst_dir)
    
    print(f"目录 '{src_dir}' 已移动到 '{dst_dir}'")

4. 文件和目录删除:斩草要除根!

  • shutil.rmtree(path, ignore_errors=False, onerror=None):递归删除整个目录树

    这个函数非常强大,但也非常危险!它可以递归地删除整个目录树,包括所有文件和子目录。使用时务必小心,确保你真的想删除这些东西。

    • ignore_errors: 如果为 True,忽略删除过程中出现的错误。
    • onerror: 一个可调用对象,用于处理删除过程中出现的错误。
    import shutil
    import os
    
    target_dir = 'my_target_directory'
    
    # 先创建一个目标目录和一些文件
    os.makedirs(target_dir, exist_ok=True)
    with open(os.path.join(target_dir, 'file1.txt'), 'w') as f:
        f.write('This is file1.')
    
    shutil.rmtree(target_dir)
    
    print(f"目录 '{target_dir}' 已被删除")

    onerror 参数的用法:

    import shutil
    import os
    
    def handle_error(function, path, excinfo):
        print(f"删除 '{path}' 时发生错误:{excinfo}")
    
    target_dir = 'my_target_directory'
    
    # 先创建一个目标目录和一些文件
    os.makedirs(target_dir, exist_ok=True)
    with open(os.path.join(target_dir, 'file1.txt'), 'w') as f:
        f.write('This is file1.')
    
    # 创建一个只读文件,模拟删除错误
    os.chmod(os.path.join(target_dir, 'file1.txt'), 0o444) # 设置为只读
    
    shutil.rmtree(target_dir, onerror=handle_error)
    
    print(f"目录 '{target_dir}' 尝试被删除(可能部分删除)")

5. 文件归档和解包:打包带走!

  • shutil.make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, dry_run=0, owner=None, group=None, logger=None):创建归档文件

    这个函数可以将一个目录或一组文件打包成归档文件,支持多种格式,比如 zip、tar、gztar、bztar、xztar。

    • base_name: 归档文件的基本名称,不包括扩展名。
    • format: 归档格式,比如 "zip", "tar", "gztar", "bztar", "xztar"。
    • root_dir: 归档的根目录。
    • base_dir: 归档的起始目录,相对于 root_dir
    import shutil
    import os
    
    source_dir = 'my_source_directory'
    archive_name = 'my_archive'
    
    # 先创建一个源目录和一些文件
    os.makedirs(source_dir, exist_ok=True)
    with open(os.path.join(source_dir, 'file1.txt'), 'w') as f:
        f.write('This is file1.')
    os.makedirs(os.path.join(source_dir, 'subdir'), exist_ok=True)
    with open(os.path.join(source_dir, 'subdir', 'file2.txt'), 'w') as f:
        f.write('This is file2.')
    
    archive_path = shutil.make_archive(archive_name, 'zip', root_dir='.', base_dir=source_dir)
    
    print(f"目录 '{source_dir}' 已归档到 '{archive_path}'")
  • shutil.unpack_archive(filename, extract_dir=None, format=None):解包归档文件

    这个函数可以将一个归档文件解包到指定的目录。

    • filename: 归档文件的路径。
    • extract_dir: 解包的目标目录,默认为当前目录。
    • format: 归档格式,如果为 None,则自动检测。
    import shutil
    import os
    
    archive_file = 'my_archive.zip'
    extract_dir = 'my_extracted_directory'
    
    shutil.unpack_archive(archive_file, extract_dir)
    
    print(f"文件 '{archive_file}' 已解包到 '{extract_dir}'")
  • shutil.get_archive_formats():获取支持的归档格式

    这个函数可以返回一个列表,包含所有支持的归档格式。

    import shutil
    
    formats = shutil.get_archive_formats()
    
    print("支持的归档格式:")
    for format_name, (description, extensions, read, write) in formats:
        print(f"  {format_name}: {description}, extensions: {extensions}")

6. 磁盘空间:你的硬盘还好吗?

  • shutil.disk_usage(path):获取磁盘使用情况

    这个函数可以返回指定路径的磁盘使用情况,包括总空间、已用空间和可用空间。

    import shutil
    
    total, used, free = shutil.disk_usage('.')
    
    print(f"总空间: {total // (2**30)} GB")
    print(f"已用空间: {used // (2**30)} GB")
    print(f"可用空间: {free // (2**30)} GB")

7. 终端大小:你的窗口有多大?

  • shutil.get_terminal_size(fallback=(columns, lines)):获取终端大小

    这个函数可以返回当前终端的列数和行数。如果无法获取终端大小,则返回 fallback 参数指定的值。

    import shutil
    
    columns, lines = shutil.get_terminal_size()
    
    print(f"终端列数: {columns}")
    print(f"终端行数: {lines}")

三、shutil 的异常处理:稳如老狗!

在使用 shutil 时,可能会遇到各种各样的异常,比如文件不存在、权限不足等。为了保证程序的健壮性,我们需要进行适当的异常处理。

常见的异常:

  • shutil.Error: 这是 shutil 模块中所有异常的基类。
  • OSError: 操作系统相关的错误,比如文件不存在、权限不足等。
  • IOError: 输入输出错误。

下面是一个简单的异常处理示例:

import shutil
import os

src_file = 'non_existent_file.txt'
dst_file = 'copy_of_non_existent_file.txt'

try:
    shutil.copy2(src_file, dst_file)
except FileNotFoundError:
    print(f"源文件 '{src_file}' 不存在!")
except PermissionError:
    print(f"没有权限复制文件!")
except shutil.Error as e:
    print(f"复制文件时发生错误:{e}")
except Exception as e:
    print(f"发生未知错误:{e}")

四、总结:shutil,文件操作的得力助手

shutil 模块提供了丰富的文件和目录操作功能,可以大大简化我们的代码,提高开发效率。无论是简单的文件复制,还是复杂的目录归档,shutil 都能胜任。掌握 shutil,你就能轻松驾驭各种文件操作场景,成为真正的文件操作大师!

五、一些使用 shutil 的小贴士:

  • 在使用 rmtree 删除目录时,务必小心,确认你要删除的目录是正确的。
  • 在使用 copytree 复制目录时,可以利用 ignore 参数过滤不需要复制的文件或目录。
  • 在处理大量文件时,可以考虑使用多线程或多进程来提高效率。
  • 熟悉各种归档格式的特点,选择合适的格式进行归档。

最后,希望今天的分享对大家有所帮助。记住,代码的世界是充满乐趣的,只要你肯学习、肯实践,就能掌握各种强大的工具,创造出令人惊叹的作品! 感谢各位的观看,下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注