Python 常用模块(四):shutil模块

目录

  • 一、shutil模块介绍
  • 二、部分常用方法介绍
    • [2.1 复制文件和目录](#2.1 复制文件和目录)
      • [2.1.1 shutil.copyfileobj()方法](#2.1.1 shutil.copyfileobj()方法)
      • [2.1.2 shutil.copyfile()方法](#2.1.2 shutil.copyfile()方法)
      • [2.1.3 shutil.copymode()方法](#2.1.3 shutil.copymode()方法)
      • [2.1.4 shutil.copystat()方法](#2.1.4 shutil.copystat()方法)
      • [2.1.5 shutil.copy()方法](#2.1.5 shutil.copy()方法)
      • [2.1.6 shutil.copy2()方法](#2.1.6 shutil.copy2()方法)
      • [2.1.7 shutil.copytree()方法](#2.1.7 shutil.copytree()方法)
    • [2.2 删除文件和目录(慎用)](#2.2 删除文件和目录(慎用))
    • [2.3 移动和重命名](#2.3 移动和重命名)
    • [2.4 打包与解包](#2.4 打包与解包)
      • [2.4.1 shutil.make_archive()方法](#2.4.1 shutil.make_archive()方法)
      • [2.4.2 shutil.unpack_archive()方法](#2.4.2 shutil.unpack_archive()方法)

一、shutil模块介绍

shutil 模块是 Python 标准库的一部分,提供了一系列高层次的文件操作功能。它在文件和目录的复制、移动、删除、打包等方面非常有用,能够简化许多文件操作的任务。

python 复制代码
In [19]: shutil?
Type:        module
String form: <module 'shutil' from 'F:\\dev_tools\\python\\python310\\lib\\shutil.py'>
File:        f:\dev_tools\python\python310\lib\shutil.py
Docstring:
Utility functions for copying and archiving files and directory trees.

XXX The functions here don't copy the resource fork or other metadata on Mac.

二、部分常用方法介绍

2.1 复制文件和目录

2.1.1 shutil.copyfileobj()方法

shutil.copyfileobj 是 Python shutil 模块中的一个方法,用于将文件内容从一个文件对象复制到另一个文件对象。它与 shutil.copyfile 不同,copyfileobj 操作的是文件对象(file-like objects),而不是文件路径。语法格式:

python 复制代码
In [21]: shutil.copyfileobj?
Signature: shutil.copyfileobj(fsrc, fdst, length=0)
Docstring: copy data from file-like object fsrc to file-like object fdst
File:      f:\dev_tools\python\python310\lib\shutil.py
Type:      function

参数说明: 
1.fsrc: 源文件对象,必须是已打开的文件对象。
2.fdst: 目标文件对象,也必须是已打开的文件对象。
# COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 64 * 1024
3.length: 每次复制的块大小(以字节为单位)。这个参数可以用于控制复制的块大小。

以下是一个简单的示例,演示如何使用 shutil.copyfileobj 将内容从一个文件复制到另一个文件:

python 复制代码
# -*- coding: utf-8 -*-
# @Time    : 2024-09-19 11:21
# @Author  : AmoXiang
# @File: shutil_copyfileobj_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680


import shutil

# 打开源文件和目标文件
with open('source.txt', 'rb') as fsrc, open('destination.txt', 'wb') as fdst:
    # 将源文件的内容复制到目标文件
    shutil.copyfileobj(fsrc, fdst)

源码:

python 复制代码
_WINDOWS = os.name == 'nt'
COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 64 * 1024

def copyfileobj(fsrc, fdst, length=0):
    """copy data from file-like object fsrc to file-like object fdst"""
    # Localize variable access to minimize overhead.
    if not length:
        length = COPY_BUFSIZE
    fsrc_read = fsrc.read
    fdst_write = fdst.write
    while True:
        buf = fsrc_read(length)
        if not buf:
            break
        fdst_write(buf)

2.1.2 shutil.copyfile()方法

shutil.copyfile(src, dst) 是 Python shutil 模块中的一个函数,用于将文件从源路径复制到目标路径。它仅复制文件的内容,不复制文件的元数据(如权限、最后修改时间)。语法格式:

python 复制代码
In [22]: shutil.copyfile?
Signature: shutil.copyfile(src, dst, *, follow_symlinks=True)
Docstring:
Copy data from src to dst in the most efficient way possible.

If follow_symlinks is not set and src is a symbolic link, a new
symlink will be created instead of copying the file it points to.
File:      f:\dev_tools\python\python310\lib\shutil.py
Type:      function
参数说明:
1.src: 源文件的路径。
2.dst: 目标文件的路径。

假设你有一个文件 source.txt,你想将其内容复制到一个新的文件 destination.txt:

python 复制代码
# -*- coding: utf-8 -*-
# @Time    : 2024-09-19 11:28
# @Author  : AmoXiang
# @File: shutil_copyfile_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680

import shutil

shutil.copyfile('source.txt', 'destination.txt')
shutil.copyfile('source.txt', '../tmp/destination.txt')

在这个例子中,source.txt 文件的内容被完整复制到 destination.txt 文件中。如果 destination.txt 文件已存在,它将被覆盖;如果不存在,则会创建该文件。只复制内容: shutil.copyfile 只复制文件的内容,不会复制文件的权限、时间戳或其他元数据。如果你需要同时复制这些元数据,可以使用 shutil.copy2。文件必须存在: src 文件必须存在,否则会抛出 FileNotFoundError 异常。dst 文件如果不存在,将会被创建。目标必须是文件: dst 参数必须是一个文件路径,而不能是一个目录。如果 dst 是一个目录,将会抛出 PermissionError 异常。覆盖行为: 如果目标文件已经存在,shutil.copyfile 将会覆盖它而不发出警告。性能考虑: shutil.copyfile 是一个底层函数,直接通过读写文件块来复制内容,性能较高。如果你不需要复制元数据,只需快速复制文件内容,那么使用 shutil.copyfile 是一个好选择。源码:

python 复制代码
def copyfile(src, dst, *, follow_symlinks=True):
    """Copy data from src to dst in the most efficient way possible.

    If follow_symlinks is not set and src is a symbolic link, a new
    symlink will be created instead of copying the file it points to.

    """
    sys.audit("shutil.copyfile", src, dst)

    if _samefile(src, dst):
        raise SameFileError("{!r} and {!r} are the same file".format(src, dst))

    file_size = 0
    for i, fn in enumerate([src, dst]):
        try:
            st = _stat(fn)
        except OSError:
            # File most likely does not exist
            pass
        else:
            # XXX What about other special files? (sockets, devices...)
            if stat.S_ISFIFO(st.st_mode):
                fn = fn.path if isinstance(fn, os.DirEntry) else fn
                raise SpecialFileError("`%s` is a named pipe" % fn)
            if _WINDOWS and i == 0:
                file_size = st.st_size

    if not follow_symlinks and _islink(src):
        os.symlink(os.readlink(src), dst)
    else:
        with open(src, 'rb') as fsrc:
            try:
                with open(dst, 'wb') as fdst:
                    # macOS
                    if _HAS_FCOPYFILE:
                        try:
                            _fastcopy_fcopyfile(fsrc, fdst, posix._COPYFILE_DATA)
                            return dst
                        except _GiveupOnFastCopy:
                            pass
                    # Linux
                    elif _USE_CP_SENDFILE:
                        try:
                            _fastcopy_sendfile(fsrc, fdst)
                            return dst
                        except _GiveupOnFastCopy:
                            pass
                    # Windows, see:
                    # https://github.com/python/cpython/pull/7160#discussion_r195405230
                    elif _WINDOWS and file_size > 0:
                        _copyfileobj_readinto(fsrc, fdst, min(file_size, COPY_BUFSIZE))
                        return dst

                    copyfileobj(fsrc, fdst)

            # Issue 43219, raise a less confusing exception
            except IsADirectoryError as e:
                if not os.path.exists(dst):
                    raise FileNotFoundError(f'Directory does not exist: {dst}') from e
                else:
                    raise

    return dst

从上面源码的 54 行可以看出本质上调用的就是 copyfileobj,所以不带元数据二进制内容复制。

2.1.3 shutil.copymode()方法

shutil.copymode() 方法是 Python shutil 模块中的一个函数,用于将源文件 (src) 的权限模式(即文件的权限位,如可读、可写、可执行等)复制到目标文件 (dst)。该函数只复制文件的权限,不会影响文件的内容或其他元数据(如文件的时间戳)。语法格式如下:

python 复制代码
In [23]: shutil.copymode?
Signature: shutil.copymode(src, dst, *, follow_symlinks=True)
Docstring:
Copy mode bits from src to dst.

If follow_symlinks is not set, symlinks aren't followed if and only
if both `src` and `dst` are symlinks.  If `lchmod` isn't available
(e.g. Linux) this method does nothing.
File:      f:\dev_tools\python\python310\lib\shutil.py
Type:      function
参数说明:
1.src: 源文件的路径。需要复制其权限模式。
2.dst: 目标文件的路径。将被赋予与源文件相同的权限模式。

假设你有一个文件 source.txt,权限设置为只读,现在你想将同样的权限复制到另一个文件 destination.txt 上:

python 复制代码
# -*- coding: utf-8 -*-
# @Time    : 2024-09-19 11:45
# @Author  : AmoXiang
# @File: shutil_copymode_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680


import shutil
import os

# 创建两个示例文件
with open('source.txt', 'w') as f:
    f.write('Hello, World!')

with open('destination.txt', 'w') as f:
    f.write('Hello, Python!')

# 将 source.txt 设为只读模式(假设在类Unix系统上)
os.chmod('source.txt', 0o444)

# 复制权限模式到目标文件
shutil.copymode('source.txt', 'destination.txt')

# 验证权限模式
print(oct(os.stat('destination.txt').st_mode)[-3:])  # 输出:'444'

在这个例子中,source.txt 被设为只读模式(权限为 444),然后 shutil.copymode 将这个权限复制到 destination.txt。详细说明:

  1. 权限模式: shutil.copymode 复制的权限模式包括用户权限(所有者、组和其他用户的读、写、执行权限),但不会复制其他元数据,如文件的时间戳、所有者等。
  2. 不改变文件内容: 该函数不会改变目标文件的内容,仅更改其权限模式。
  3. 适用平台: shutil.copymode 的效果在类 Unix 系统(如 Linux 和 macOS)上表现最佳,因为这些系统有详细的文件权限设置。在 Windows 上,权限的处理机制有所不同,可能不会完全应用或显示出同样的效果。

使用场景:

  1. 同步文件权限:在复制文件内容后,希望确保两个文件的权限一致。
  2. 权限管理:在处理文件的备份或迁移时,保持原有文件的权限设置。

小结:shutil.copymode() 方法是一个用于复制文件权限模式的函数。它只复制权限,不影响文件内容或其他元数据。常用于需要保持文件权限一致的场景,如文件备份、迁移或同步。源码:

python 复制代码
def copymode(src, dst, *, follow_symlinks=True):
    """Copy mode bits from src to dst.

    If follow_symlinks is not set, symlinks aren't followed if and only
    if both `src` and `dst` are symlinks.  If `lchmod` isn't available
    (e.g. Linux) this method does nothing.

    """
    sys.audit("shutil.copymode", src, dst)

    if not follow_symlinks and _islink(src) and os.path.islink(dst):
        if hasattr(os, 'lchmod'):
            stat_func, chmod_func = os.lstat, os.lchmod
        else:
            return
    else:
        stat_func, chmod_func = _stat, os.chmod

    st = stat_func(src)
    chmod_func(dst, stat.S_IMODE(st.st_mode))

2.1.4 shutil.copystat()方法

shutil.copystat() 方法是 shutil 模块中的一个方法,用于将源文件(src)的所有状态信息(如权限、最后访问时间、最后修改时间)复制到目标文件(dst)。但是,它不会复制文件内容或文件的实际数据,仅复制文件的元数据。基本语法:

python 复制代码
In [24]: shutil.copystat?
Signature: shutil.copystat(src, dst, *, follow_symlinks=True)
Docstring:
Copy file metadata

Copy the permission bits, last access time, last modification time, and
flags from `src` to `dst`. On Linux, copystat() also copies the "extended
attributes" where possible. The file contents, owner, and group are
unaffected. `src` and `dst` are path-like objects or path names given as
strings.

If the optional flag `follow_symlinks` is not set, symlinks aren't
followed if and only if both `src` and `dst` are symlinks.
File:      f:\dev_tools\python\python310\lib\shutil.py
Type:      function

假设你有一个文件 source.txt,你想要复制它的状态信息到另一个文件 destination.txt:

python 复制代码
# -*- coding: utf-8 -*-
# @Time    : 2024-09-19 11:48
# @Author  : AmoXiang
# @File: shutil.copystat_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680

import shutil
import os

# 创建示例文件
with open('source.txt', 'w') as f:
    f.write('Hello, World!')

with open('destination.txt', 'w') as f:
    f.write('Hello, Python!')

# 复制文件状态
shutil.copystat('source.txt', 'destination.txt')

# 查看文件属性(例如最后修改时间)
# Source last modified: 1726717818.6047208
print("Source last modified:", os.path.getmtime('source.txt'))
# Destination last modified: 1726717818.6047208
print("Destination last modified:", os.path.getmtime('destination.txt'))

在这个例子中,shutil.copystat 将 source.txt 的元数据(如权限和时间戳)复制到了 destination.txt。详细说明:

  1. 权限: shutil.copystat 会复制文件的权限信息,包括所有权和组信息(如果操作系统支持)。
  2. 时间戳: 包括最后访问时间(atime)和最后修改时间(mtime)。
  3. 不复制的内容: shutil.copystat 只复制元数据,不会复制文件内容。如果需要同时复制内容和状态信息,可以先使用 shutil.copy2,它会复制内容并保留状态信息。

源码:

python 复制代码
def copystat(src, dst, *, follow_symlinks=True):
    """Copy file metadata

    Copy the permission bits, last access time, last modification time, and
    flags from `src` to `dst`. On Linux, copystat() also copies the "extended
    attributes" where possible. The file contents, owner, and group are
    unaffected. `src` and `dst` are path-like objects or path names given as
    strings.

    If the optional flag `follow_symlinks` is not set, symlinks aren't
    followed if and only if both `src` and `dst` are symlinks.
    """
    sys.audit("shutil.copystat", src, dst)

    def _nop(*args, ns=None, follow_symlinks=None):
        pass

    # follow symlinks (aka don't not follow symlinks)
    follow = follow_symlinks or not (_islink(src) and os.path.islink(dst))
    if follow:
        # use the real function if it exists
        def lookup(name):
            return getattr(os, name, _nop)
    else:
        # use the real function only if it exists
        # *and* it supports follow_symlinks
        def lookup(name):
            fn = getattr(os, name, _nop)
            if fn in os.supports_follow_symlinks:
                return fn
            return _nop

    if isinstance(src, os.DirEntry):
        st = src.stat(follow_symlinks=follow)
    else:
        st = lookup("stat")(src, follow_symlinks=follow)
    mode = stat.S_IMODE(st.st_mode)
    lookup("utime")(dst, ns=(st.st_atime_ns, st.st_mtime_ns),
        follow_symlinks=follow)
    # We must copy extended attributes before the file is (potentially)
    # chmod()'ed read-only, otherwise setxattr() will error with -EACCES.
    _copyxattr(src, dst, follow_symlinks=follow)
    try:
        lookup("chmod")(dst, mode, follow_symlinks=follow)
    except NotImplementedError:
        # if we got a NotImplementedError, it's because
        #   * follow_symlinks=False,
        #   * lchown() is unavailable, and
        #   * either
        #       * fchownat() is unavailable or
        #       * fchownat() doesn't implement AT_SYMLINK_NOFOLLOW.
        #         (it returned ENOSUP.)
        # therefore we're out of options--we simply cannot chown the
        # symlink.  give up, suppress the error.
        # (which is what shutil always did in this circumstance.)
        pass
    if hasattr(st, 'st_flags'):
        try:
            lookup("chflags")(dst, st.st_flags, follow_symlinks=follow)
        except OSError as why:
            for err in 'EOPNOTSUPP', 'ENOTSUP':
                if hasattr(errno, err) and why.errno == getattr(errno, err):
                    break
            else:
                raise

2.1.5 shutil.copy()方法

shutil.copy() 方法是 Python shutil 模块中的一个函数,用于将源文件 (src) 复制到目标位置 (dst)。与 shutil.copyfile 不同的是,shutil.copy 除了复制文件的内容外,还会复制文件的权限位(mode bits),但不会复制其他的元数据(如文件的时间戳、所有者等)。基础语法:

python 复制代码
In [25]: shutil.copy?
Signature: shutil.copy(src, dst, *, follow_symlinks=True)
Docstring:
Copy data and mode bits ("cp src dst"). Return the file's destination.

The destination may be a directory.

If follow_symlinks is false, symlinks won't be followed. This
resembles GNU's "cp -P src dst".

If source and destination are the same file, a SameFileError will be
raised.
File:      f:\dev_tools\python\python310\lib\shutil.py
Type:      function
参数说明: 
1.src: 源文件的路径。
2.dst: 目标文件或目标目录的路径。如果 dst 是目录,则文件将被复制到该目录中,并保持原文件名。

示例:

python 复制代码
# -*- coding: utf-8 -*-
# @Time    : 2024-09-19 12:23
# @Author  : AmoXiang
# @File: shutil_copy_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680

import shutil

# 复制文件,并保留权限
shutil.copy('source.txt', 'des2.txt')
# 复制文件到目录
# 如果目标路径是一个目录,那么文件将被复制到该目录下,并保持原始文件名
shutil.copy('source.txt', './tmp')

详细说明:

  1. 复制内容和权限: shutil.copy 会复制文件的内容和权限位。复制后的文件在目标位置拥有与源文件相同的读、写、执行权限。
  2. 目标文件存在时的行为: 如果目标文件已经存在,shutil.copy 会覆盖该文件,而不会发出任何警告。
  3. 目标路径可以是文件或目录: 如果 dst 是一个文件路径,源文件将被复制并重命名。如果 dst 是一个目录,文件将被复制到该目录下,文件名保持不变。
  4. 不复制元数据: 除了权限外,shutil.copy 不会复制其他文件元数据,如文件的所有者、组、时间戳等。如果需要复制这些信息,可以使用 shutil.copy2。

源码:

python 复制代码
def copy(src, dst, *, follow_symlinks=True):
    """Copy data and mode bits ("cp src dst"). Return the file's destination.

    The destination may be a directory.

    If follow_symlinks is false, symlinks won't be followed. This
    resembles GNU's "cp -P src dst".

    If source and destination are the same file, a SameFileError will be
    raised.

    """
    if os.path.isdir(dst):
        dst = os.path.join(dst, os.path.basename(src))
    copyfile(src, dst, follow_symlinks=follow_symlinks)
    copymode(src, dst, follow_symlinks=follow_symlinks)
    return dst

从源码中可以看出,本质其实就是调用的 copyfile() 方法与 copymode() 方法。

2.1.6 shutil.copy2()方法

shutil.copy2() 方法是 Python shutil 模块中的一个函数,用于复制文件的内容、权限位以及所有可用的元数据(如文件的访问时间、修改时间等)到目标路径。它可以看作是 shutil.copy 的增强版,因为除了复制文件内容和权限外,它还会复制更多的元数据。基本语法:

python 复制代码
In [26]: shutil.copy2?
Signature: shutil.copy2(src, dst, *, follow_symlinks=True)
Docstring:
Copy data and metadata. Return the file's destination.

Metadata is copied with copystat(). Please see the copystat function
for more information.

The destination may be a directory.

If follow_symlinks is false, symlinks won't be followed. This
resembles GNU's "cp -P src dst".
File:      f:\dev_tools\python\python310\lib\shutil.py
Type:      function
参数说明:
1.src: 源文件的路径。
2.dst: 目标文件或目标目录的路径。如果 dst 是目录,则文件将被复制到该目录中,并保持原文件名。

示例代码:

python 复制代码
# -*- coding: utf-8 -*-
# @Time    : 2024-09-19 12:36
# @Author  : AmoXiang
# @File: shutil_copy2_demo.py.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680

import shutil
import os
import time

# 复制文件内容、权限和元数据
shutil.copy2('source.txt', 'des3.txt')
shutil.copy2('source.txt', './tmp')

# 创建源文件并设置时间戳
with open('s1.txt', 'w') as f:
    f.write('Hello, World!')

# 修改文件的时间戳
os.utime('s1.txt', (time.time() - 10000, time.time() - 10000))

# 复制文件内容、权限和元数据
shutil.copy2('s1.txt', 'd1.txt')

# 验证复制后的时间戳
print("Source file mtime:", os.path.getmtime('s1.txt'))
print("Destination file mtime:", os.path.getmtime('d1.txt'))

源码:

python 复制代码
def copy2(src, dst, *, follow_symlinks=True):
    """Copy data and metadata. Return the file's destination.

    Metadata is copied with copystat(). Please see the copystat function
    for more information.

    The destination may be a directory.

    If follow_symlinks is false, symlinks won't be followed. This
    resembles GNU's "cp -P src dst".
    """
    if os.path.isdir(dst):
        dst = os.path.join(dst, os.path.basename(src))
    copyfile(src, dst, follow_symlinks=follow_symlinks)
    copystat(src, dst, follow_symlinks=follow_symlinks)
    return dst

从源码中可以看出,本质其实就是调用的 copyfile() 方法与 copystat() 方法。shutil.copy2 与其他复制函数的对比:

  1. shutil.copyfile(src, dst):仅复制文件内容,不复制权限或元数据。
  2. shutil.copy(src, dst):复制文件内容和权限,但不复制其他元数据。
  3. shutil.copy2(src, dst):复制文件内容、权限和所有可用的元数据(包括时间戳)。

小结: shutil.copy2(src, dst) 是一个全面的文件复制函数,用于复制文件的内容、权限以及所有元数据。适用于需要完整复制文件及其属性的场景,如备份或迁移文件时保留所有文件属性。如果你只需要复制文件内容而不需要元数据,可以使用 shutil.copyfile。如果需要复制内容和权限但不需要其他元数据,可以使用 shutil.copy。

2.1.7 shutil.copytree()方法

shutil.copytree() 方法是 Python shutil 模块中的一个函数,用于递归地复制整个目录树(包括目录及其所有内容)到目标位置。基本语法:

python 复制代码
In [27]: shutil.copytree?
Signature:
shutil.copytree(
    src,
    dst,
    symlinks=False,
    ignore=None,
    copy_function=<function copy2 at 0x000001C48B5B8EE0>,
    ignore_dangling_symlinks=False,
    dirs_exist_ok=False,
)
Docstring:
Recursively copy a directory tree and return the destination directory.

If exception(s) occur, an Error is raised with a list of reasons.

If the optional symlinks flag is true, symbolic links in the
source tree result in symbolic links in the destination tree; if
it is false, the contents of the files pointed to by symbolic
links are copied. If the file pointed by the symlink doesn't
exist, an exception will be added in the list of errors raised in
an Error exception at the end of the copy process.

You can set the optional ignore_dangling_symlinks flag to true if you
want to silence this exception. Notice that this has no effect on
platforms that don't support os.symlink.

The optional ignore argument is a callable. If given, it
is called with the `src` parameter, which is the directory
being visited by copytree(), and `names` which is the list of
`src` contents, as returned by os.listdir():

    callable(src, names) -> ignored_names

Since copytree() is called recursively, the callable will be
called once for each directory that is copied. It returns a
list of names relative to the `src` directory that should
not be copied.

The optional copy_function argument is a callable that will be used
to copy each file. It will be called with the source path and the
destination path as arguments. By default, copy2() is used, but any
function that supports the same signature (like copy()) can be used.

If dirs_exist_ok is false (the default) and `dst` already exists, a
`FileExistsError` is raised. If `dirs_exist_ok` is true, the copying
operation will continue if it encounters existing directories, and files
within the `dst` tree will be overwritten by corresponding files from the
`src` tree.
File:      f:\dev_tools\python\python310\lib\shutil.py
Type:      function
参数说明: 
1.src: 源目录的路径,表示要复制的目录树的根目录。
2.dst: 目标目录的路径,表示复制的目录树将放置的位置。目标目录必须不存在,否则会抛出 FileExistsError。
3.symlinks (默认 False): 如果为 True,则复制符号链接本身而不是其指向的实际文件。
4.ignore (默认 None): 可以是一个函数,接收目录和内容列表作为参数,并返回要忽略的内容的列表。通常用于跳过某些文件或目录。
5.copy_function (默认 shutil.copy2): 用于复制文件的函数,可以是 shutil.copy, shutil.copy2,或者自定义的函数。
6.ignore_dangling_symlinks (默认 False): 如果为 True,在处理指向不存在文件的符号链接时不会报错。
7.dirs_exist_ok (Python 3.8+,默认 False): 如果为 True,目标目录已存在时不会报错,会将源目录树的内容复制到现有的
目标目录中。

示例:

python 复制代码
# -*- coding: utf-8 -*-
# @Time    : 2024-09-19 12:48
# @Author  : AmoXiang
# @File: shutil_copytree_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680

import shutil

# 递归地复制整个目录树
# FileExistsError: [WinError 183] 当文件已存在时,无法创建该文件。: './tmp'
# shutil.copytree('.', './tmp')
# 删了tmp目录,重新执行该代码
# shutil.copytree('.', './tmp')

# ignore: 忽略特定文件或目录

# def ignore_files(dir_name, files):
#     return [f for f in files if f.endswith('.txt')]
#
#
# shutil.copytree('.', './tmp', ignore=ignore_files)


# 使用 ignore_patterns 忽略 .py 文件
shutil.copytree('.', './tmp', ignore=shutil.ignore_patterns('*.py'), dirs_exist_ok=True)

源码:

python 复制代码
def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2,
             ignore_dangling_symlinks=False, dirs_exist_ok=False):
    """Recursively copy a directory tree and return the destination directory.

    If exception(s) occur, an Error is raised with a list of reasons.

    If the optional symlinks flag is true, symbolic links in the
    source tree result in symbolic links in the destination tree; if
    it is false, the contents of the files pointed to by symbolic
    links are copied. If the file pointed by the symlink doesn't
    exist, an exception will be added in the list of errors raised in
    an Error exception at the end of the copy process.

    You can set the optional ignore_dangling_symlinks flag to true if you
    want to silence this exception. Notice that this has no effect on
    platforms that don't support os.symlink.

    The optional ignore argument is a callable. If given, it
    is called with the `src` parameter, which is the directory
    being visited by copytree(), and `names` which is the list of
    `src` contents, as returned by os.listdir():

        callable(src, names) -> ignored_names

    Since copytree() is called recursively, the callable will be
    called once for each directory that is copied. It returns a
    list of names relative to the `src` directory that should
    not be copied.

    The optional copy_function argument is a callable that will be used
    to copy each file. It will be called with the source path and the
    destination path as arguments. By default, copy2() is used, but any
    function that supports the same signature (like copy()) can be used.

    If dirs_exist_ok is false (the default) and `dst` already exists, a
    `FileExistsError` is raised. If `dirs_exist_ok` is true, the copying
    operation will continue if it encounters existing directories, and files
    within the `dst` tree will be overwritten by corresponding files from the
    `src` tree.
    """
    sys.audit("shutil.copytree", src, dst)
    with os.scandir(src) as itr:
        entries = list(itr)
    return _copytree(entries=entries, src=src, dst=dst, symlinks=symlinks,
                     ignore=ignore, copy_function=copy_function,
                     ignore_dangling_symlinks=ignore_dangling_symlinks,
                     dirs_exist_ok=dirs_exist_ok)


def _copytree(entries, src, dst, symlinks, ignore, copy_function,
              ignore_dangling_symlinks, dirs_exist_ok=False):
    if ignore is not None:
        ignored_names = ignore(os.fspath(src), [x.name for x in entries])
    else:
        ignored_names = set()

    os.makedirs(dst, exist_ok=dirs_exist_ok)
    errors = []
    use_srcentry = copy_function is copy2 or copy_function is copy

    for srcentry in entries:
        if srcentry.name in ignored_names:
            continue
        srcname = os.path.join(src, srcentry.name)
        dstname = os.path.join(dst, srcentry.name)
        srcobj = srcentry if use_srcentry else srcname
        try:
            is_symlink = srcentry.is_symlink()
            if is_symlink and os.name == 'nt':
                # Special check for directory junctions, which appear as
                # symlinks but we want to recurse.
                lstat = srcentry.stat(follow_symlinks=False)
                if lstat.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT:
                    is_symlink = False
            if is_symlink:
                linkto = os.readlink(srcname)
                if symlinks:
                    # We can't just leave it to `copy_function` because legacy
                    # code with a custom `copy_function` may rely on copytree
                    # doing the right thing.
                    os.symlink(linkto, dstname)
                    copystat(srcobj, dstname, follow_symlinks=not symlinks)
                else:
                    # ignore dangling symlink if the flag is on
                    if not os.path.exists(linkto) and ignore_dangling_symlinks:
                        continue
                    # otherwise let the copy occur. copy2 will raise an error
                    if srcentry.is_dir():
                        copytree(srcobj, dstname, symlinks, ignore,
                                 copy_function, ignore_dangling_symlinks,
                                 dirs_exist_ok)
                    else:
                        copy_function(srcobj, dstname)
            elif srcentry.is_dir():
                copytree(srcobj, dstname, symlinks, ignore, copy_function,
                         ignore_dangling_symlinks, dirs_exist_ok)
            else:
                # Will raise a SpecialFileError for unsupported file types
                copy_function(srcobj, dstname)
        # catch the Error from the recursive copytree so that we can
        # continue with other files
        except Error as err:
            errors.extend(err.args[0])
        except OSError as why:
            errors.append((srcname, dstname, str(why)))
    try:
        copystat(src, dst)
    except OSError as why:
        # Copying file access times may fail on Windows
        if getattr(why, 'winerror', None) is None:
            errors.append((src, dst, str(why)))
    if errors:
        raise Error(errors)
    return dst

小结: 递归复制目录。默认使用 copy2,也就是带更多的元数据复制。src、dst 必须是目录,src 必须存在,dst 必须不存在,ignore = func ,提供一个 callable(src, names) -> ignored_names。提供一个函数,它会被调用。src 是源目录,names 是 os.listdir(src) 的结果,就是列出 src 中的文件名,返回值是要被过滤的文件名的 set 类型数据。

2.2 删除文件和目录(慎用)

递归删除整个目录树,path 必须是一个目录。如果 ignore_errors=True,则忽略删除过程中遇到的错误。可以通过 onerror 指定错误处理函数。

python 复制代码
In [28]: shutil.rmtree?
Signature: shutil.rmtree(path, ignore_errors=False, onerror=None)
Docstring:
Recursively delete a directory tree.

If ignore_errors is set, errors are ignored; otherwise, if onerror
is set, it is called to handle the error with arguments (func,
path, exc_info) where func is platform and implementation dependent;
path is the argument to that function that caused it to fail; and
exc_info is a tuple returned by sys.exc_info().  If ignore_errors
is false and onerror is None, an exception is raised.
File:      f:\dev_tools\python\python310\lib\shutil.py
Type:      function

示例:

python 复制代码
# -*- coding: utf-8 -*-
# @Time    : 2024-09-19 13:09
# @Author  : AmoXiang
# @File: shutil_rmtree_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680

import shutil

# NotADirectoryError: [WinError 267] 目录名称无效。: './s1.txt'
# shutil.rmtree('./s1.txt')  # 删除整个目录树

shutil.rmtree('./tmp')

小结:递归删除。如同 rm -rf 一样危险,慎用。它不是原子操作,有可能删除错误,就会中断,已经删除的就删除了。ignore_errors 为 True,忽略错误。当为 False 或者 omitted 时 onerror 生效。onerror 为 callable,接受函数 function、path 和 execinfo。源码:

python 复制代码
def rmtree(path, ignore_errors=False, onerror=None):
    """Recursively delete a directory tree.

    If ignore_errors is set, errors are ignored; otherwise, if onerror
    is set, it is called to handle the error with arguments (func,
    path, exc_info) where func is platform and implementation dependent;
    path is the argument to that function that caused it to fail; and
    exc_info is a tuple returned by sys.exc_info().  If ignore_errors
    is false and onerror is None, an exception is raised.

    """
    sys.audit("shutil.rmtree", path)
    if ignore_errors:
        def onerror(*args):
            pass
    elif onerror is None:
        def onerror(*args):
            raise
    if _use_fd_functions:
        # While the unsafe rmtree works fine on bytes, the fd based does not.
        if isinstance(path, bytes):
            path = os.fsdecode(path)
        # Note: To guard against symlink races, we use the standard
        # lstat()/open()/fstat() trick.
        try:
            orig_st = os.lstat(path)
        except Exception:
            onerror(os.lstat, path, sys.exc_info())
            return
        try:
            fd = os.open(path, os.O_RDONLY)
            fd_closed = False
        except Exception:
            onerror(os.open, path, sys.exc_info())
            return
        try:
            if os.path.samestat(orig_st, os.fstat(fd)):
                _rmtree_safe_fd(fd, path, onerror)
                try:
                    os.close(fd)
                    fd_closed = True
                    os.rmdir(path)
                except OSError:
                    onerror(os.rmdir, path, sys.exc_info())
            else:
                try:
                    # symlinks to directories are forbidden, see bug #1669
                    raise OSError("Cannot call rmtree on a symbolic link")
                except OSError:
                    onerror(os.path.islink, path, sys.exc_info())
        finally:
            if not fd_closed:
                os.close(fd)
    else:
        try:
            if _rmtree_islink(path):
                # symlinks to directories are forbidden, see bug #1669
                raise OSError("Cannot call rmtree on a symbolic link")
        except OSError:
            onerror(os.path.islink, path, sys.exc_info())
            # can't continue even if onerror hook returns
            return
        return _rmtree_unsafe(path, onerror)

2.3 移动和重命名

shutil.move() 方法移动文件或目录到新的位置,或者重命名。如果 dst 是一个目录,文件或目录将移动到该目录下。

python 复制代码
In [29]: shutil.move?
Signature: shutil.move(src, dst, copy_function=<function copy2 at 0x000001C48B5B8EE0>)
Docstring:
Recursively move a file or directory to another location. This is
similar to the Unix "mv" command. Return the file or directory's
destination.

If the destination is a directory or a symlink to a directory, the source
is moved inside the directory. The destination path must not already
exist.

If the destination already exists but is not a directory, it may be
overwritten depending on os.rename() semantics.

If the destination is on our current filesystem, then rename() is used.
Otherwise, src is copied to the destination and then removed. Symlinks are
recreated under the new name if os.rename() fails because of cross
filesystem renames.

The optional `copy_function` argument is a callable that will be used
to copy the source or it will be delegated to `copytree`.
By default, copy2() is used, but any function that supports the same
signature (like copy()) can be used.

A lot more could be done here...  A look at a mv.c shows a lot of
the issues this implementation glosses over.
File:      f:\dev_tools\python\python310\lib\shutil.py
Type:      function

示例:

python 复制代码
# -*- coding: utf-8 -*-
# @Time    : 2024-09-19 13:17
# @Author  : AmoXiang
# @File: shutil_move_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680


import shutil

shutil.move('s1.txt', 's1_bak.txt')  # 重命名文件
shutil.move('../tmp', './')  # 移动目录

源码:

python 复制代码
def move(src, dst, copy_function=copy2):
    """Recursively move a file or directory to another location. This is
    similar to the Unix "mv" command. Return the file or directory's
    destination.

    If the destination is a directory or a symlink to a directory, the source
    is moved inside the directory. The destination path must not already
    exist.

    If the destination already exists but is not a directory, it may be
    overwritten depending on os.rename() semantics.

    If the destination is on our current filesystem, then rename() is used.
    Otherwise, src is copied to the destination and then removed. Symlinks are
    recreated under the new name if os.rename() fails because of cross
    filesystem renames.

    The optional `copy_function` argument is a callable that will be used
    to copy the source or it will be delegated to `copytree`.
    By default, copy2() is used, but any function that supports the same
    signature (like copy()) can be used.

    A lot more could be done here...  A look at a mv.c shows a lot of
    the issues this implementation glosses over.

    """
    sys.audit("shutil.move", src, dst)
    real_dst = dst
    if os.path.isdir(dst):
        if _samefile(src, dst):
            # We might be on a case insensitive filesystem,
            # perform the rename anyway.
            os.rename(src, dst)
            return

        # Using _basename instead of os.path.basename is important, as we must
        # ignore any trailing slash to avoid the basename returning ''
        real_dst = os.path.join(dst, _basename(src))

        if os.path.exists(real_dst):
            raise Error("Destination path '%s' already exists" % real_dst)
    try:
        os.rename(src, real_dst)
    except OSError:
        if os.path.islink(src):
            linkto = os.readlink(src)
            os.symlink(linkto, real_dst)
            os.unlink(src)
        elif os.path.isdir(src):
            if _destinsrc(src, dst):
                raise Error("Cannot move a directory '%s' into itself"
                            " '%s'." % (src, dst))
            if (_is_immutable(src)
                    or (not os.access(src, os.W_OK) and os.listdir(src)
                        and sys.platform == 'darwin')):
                raise PermissionError("Cannot move the non-empty directory "
                                      "'%s': Lacking write permission to '%s'."
                                      % (src, src))
            copytree(src, real_dst, copy_function=copy_function,
                     symlinks=True)
            rmtree(src)
        else:
            copy_function(src, real_dst)
            os.unlink(src)
    return real_dst

2.4 打包与解包

2.4.1 shutil.make_archive()方法

shutil.make_archive 是 Python 的 shutil 模块中一个用于创建压缩包的函数。它可以将指定的目录或文件打包成一个压缩文件,支持多种格式,如 ZIP、TAR、GZIP 等。基础语法:

python 复制代码
In [30]: shutil.make_archive?
Signature:
shutil.make_archive(
    base_name,
    format,
    root_dir=None,
    base_dir=None,
    verbose=0,
    dry_run=0,
    owner=None,
    group=None,
    logger=None,
)
Docstring:
Create an archive file (eg. zip or tar).

'base_name' is the name of the file to create, minus any format-specific
extension; 'format' is the archive format: one of "zip", "tar", "gztar",
"bztar", or "xztar".  Or any other registered format.

'root_dir' is a directory that will be the root directory of the
archive; ie. we typically chdir into 'root_dir' before creating the
archive.  'base_dir' is the directory where we start archiving from;
ie. 'base_dir' will be the common prefix of all files and
directories in the archive.  'root_dir' and 'base_dir' both default
to the current directory.  Returns the name of the archive file.

'owner' and 'group' are used when creating a tar archive. By default,
uses the current owner and group.
File:      f:\dev_tools\python\python310\lib\shutil.py
Type:      function
参数简单说明:
1.base_name: 压缩包的文件名或路径(不带扩展名)。压缩文件将存储在此路径中。
2.format: 压缩格式的类型,可以是 zip、tar、gztar、bztar、xztar 等。
3.root_dir: 要打包的目录路径。该目录及其内容将被打包成压缩文件。

示例:

python 复制代码
# -*- coding: utf-8 -*-
# @Time    : 2024-09-19 15:23
# @Author  : AmoXiang
# @File: shutil_make_archive_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680

import shutil

# 创建一个 ZIP 压缩包
'''
base_name: 如果提供了完整路径,压缩包会生成在该路径中。
如果只提供了文件名,则压缩包会生成在当前工作目录中。

format: zip、tar、gztar、bztar、xztar

root_dir: 指定要打包的目录。该目录及其内容会被递归打包。
如果未指定,则会将当前工作目录作为打包的根目录。
'''
shutil.make_archive('../test', 'zip', '../shutil_demo')
# 这个例子中,只会将 shutil_demo/dic1 中的内容打包到 test.zip 中,而不是整个 shutil_demo。
shutil.make_archive('../test', 'zip', '../shutil_demo', 'dic1')

2.4.2 shutil.unpack_archive()方法

解压缩文件并将内容提取到指定目录。filename 是压缩文件的路径,extract_dir 是解压缩目标目录(默认为当前目录),format 可以指定压缩格式。

python 复制代码
In [31]: shutil.unpack_archive?
Signature: shutil.unpack_archive(filename, extract_dir=None, format=None)
Docstring:
Unpack an archive.

`filename` is the name of the archive.

`extract_dir` is the name of the target directory, where the archive
is unpacked. If not provided, the current working directory is used.

`format` is the archive format: one of "zip", "tar", "gztar", "bztar",
or "xztar".  Or any other registered format.  If not provided,
unpack_archive will use the filename extension and see if an unpacker
was registered for that extension.

In case none is found, a ValueError is raised.
File:      f:\dev_tools\python\python310\lib\shutil.py
Type:      function
参数说明:
1.filename: 压缩文件的路径。
2.extract_dir (可选): 解压到的目标目录。如果不指定,文件将解压到当前工作目录中。
3.format (可选): 指定压缩文件的格式,如 'zip', 'tar', 'gztar', 'bztar', 'xztar' 等。
一般情况下不需要指定,shutil.unpack_archive 会根据文件扩展名自动识别格式。

示例:

python 复制代码
# -*- coding: utf-8 -*-
# @Time    : 2024-09-19 15:41
# @Author  : AmoXiang
# @File: shutil_unpack_archive_demo.py
# @Software: PyCharm
# @Blog: https://blog.csdn.net/xw1680
import shutil

# 解压 my_archive.tar.gz 到当前目录
'''
在某些情况下,文件扩展名可能无法正确反映其格式,此时你可以显式指定格式。(最好是指定)
'''
shutil.unpack_archive('../test.zip', '../', 'zip')
相关推荐
苹果酱056711 分钟前
一文读懂SpringCLoud
java·开发语言·spring boot·后端·中间件
Eoneanyna12 分钟前
QT设置git仓库
开发语言·git·qt
小鹿( ﹡ˆoˆ﹡ )17 分钟前
Python中的树与图:构建复杂数据结构的艺术
开发语言·python
阡之尘埃22 分钟前
Python数据分析案例59——基于图神经网络的反欺诈交易检测(GCN,GAT,GIN)
python·神经网络·数据挖掘·数据分析·图神经网络·反欺诈·风控大数据
想变成自大狂22 分钟前
C++中的异构容器
开发语言·c++
qq_1728055923 分钟前
GO GIN 推荐的库
开发语言·golang·gin
xiaojiesec27 分钟前
第157天: 安全开发-Python 自动化挖掘项目&SRC 目标&FOFA 资产&Web 爬虫解析库
python·安全
friklogff30 分钟前
【C#生态园】构建你的C#操作系统:框架选择与实践
服务器·开发语言·c#
27划流星雨_31 分钟前
from tqdm.auto import tqdm用法详细介绍
python
爱里承欢。36 分钟前
【Python语言初识(二)】
python