[Python学习日记-52] Python 中的 copy 模块 —— shutil

[Python学习日记-52] Python 中的 copy 模块 —— shutil

简介

shutil 模块

简介

        在前面的学习当中,我们学习了如何在 Python 中创建文件,这个时候我们基本已经有了写程序的能力了,而有的时候我们也想使用 Python 来对文件进行更多的操作,例如修改它的权限信息、存储位置等,同时在传递程序或者提供程序下载时,我们更希望的是程序能越小越好,这样对于用户传输的带宽要求、等待时间等都会更友好,这就要用到 shutil 模块了,下面我们一起来看一看吧。

shutil 模块

        该模块是高级的文件、目录、压缩包的处理模块

一、copyfileobj()

        将文件内容拷贝到另一个文件中,语法如下

shutil.copyfileobj(src_file_obj, dest_file_obj, length)

参数说明:

  • src_file_obj:源文件对象
  • dest_file_obj:目标文件对象
  • length:可选参数,指定要复制的字节数,默认为-1,表示复制整个文件。如果指定了 length 参数,函数将在复制指定字节数后停止
  • 演示代码如下

    import shutil
    
    shutil.copyfileobj(open('game.json','r'),open('game_new.json','w'))

    代码效果如下:

    二、copyfile()

            拷贝整个文件,如果目标文件不存在则会直接创建,语法如下

    shutil.copyfile(src, dst, *, follow_symlinks=True)

    参数说明:

  • src: 需要复制的源文件路径
  • dst: 目标文件路径
  • follow_symlinks: 如果为 True(默认值),则复制文件时会跟踪源文件的符号链接。如果为 False,则复制源文件本身
  •         如果复制操作成功,则返回目标文件的路径,演示代码如下

    import shutil
    
    shutil.copyfile("game.json","game1.json")   # 目标文件无需存在

    代码效果如下:

    三、copymode()

            仅拷贝权限,而内容、组、用户均不变,语法如下

    shutil.copymode(src, dst)

    参数说明:

  • src: 需要复制的源文件路径
  • dst: 目标文件路径
  • 演示代码如下

    import shutil
    
    shutil.copymode("game.json","game1.json")    # 目标文件必须存在

            该模块对于 Linux 系统中的权限操作比较好使,对于 Windows 的权限操作该模块实测并无效果。

    四、copystat()

            仅拷贝状态信息,包括:mode bits,atime,mtime,flags,语法如下

    shutil.copystat(src, dst, *, follow_symlinks=True)

    参数说明:

  • src:需要复制权限和状态信息的源文件路径
  • dst:目标文件路径
  • follow_symlinks:如果为 True,则复制符号链接的状态信息;如果为 False,则复制符号链接本身的状态信息,默认为 True
  •         该函数用于将 src 的文件权限和状态信息复制到 dst 中。它会复制文件的权限位,最后访问时间,最后修改时间和用户 id/group_id,演示代码如下

    import shutil
    
    shutil.copystat("game.json","game1.json")    # 目标文件必须存在

    代码效果如下:

            在 Windows 下只能复制修改时间,该模块与 copymode() 一样,在 Linux 系统中的权限操作比较好使。

    五、copy()

            拷贝文件或目录和权限,语法如下

    shutil.copy(src, dst, *, follow_symlinks=True)

    参数说明:

  • src:是源文件的路径
  • dst:是目标文件的路径
  • follow_symlinks:表示是否跟踪符号链接,默认为 True
  •         该方法是将源文件复制到目标文件,并返回目标文件的路径,演示代码如下

    import shutil
    
    print(shutil.copy("game.json","game1.json"))

    代码输出如下:

            但它并不能复制整个目录,如果需要的话可以使用 copytree() 方法。

    六、copy2()

            拷贝文件或目录和状态信息,语法如下

    shutil.copy2(src, dst, *, follow_symlinks=True)

    参数说明:

  • src:是源文件的路径
  • dst:是目标文件的路径
  • follow_symlinks:表示是否跟踪符号链接,默认为 True
  • 演示代码如下

    import shutil
    
    print(shutil.copy2("game.json","game1.json"))

    代码输出如下:

    七、ignore_patterns() & copytree()

            copytree() 方法可递归的去拷贝目录,通常会配合 ignore_patterns() 方法使用,可以对不想拷贝的文件进行过滤,语法如下

    shutil.copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, ignore_dangling_symlinks=False)

    shutil.ignore_patterns(*patterns)

    copytree() 的参数说明:

  • src:源目录的路径
  • dst:目标目录的路径
  • symlinks:如果为 True,则拷贝符号链接。默认为 False
  • ignore:配合 ignore_patterns() 使用,用于指定要忽略的文件和目录的规则。如果不指定,则默认不忽略任何文件和目录
  • copy_function:复制文件的函数。默认为 shutil.copy2(),它会复制文件的权限和其他状态信息。
  • ignore_dangling_symlinks:如果为 True,则忽略源目录中的悬挂符号链接。默认为 False。
  • ignore_patterns() 的参数说明:

  • patterns:是一个或多个glob模式字符串,可以使用通配符(例如 * 和 ?)来匹配文件和目录的名称
  • ignore_patterns() 会根据这些模式创建一个用于过滤名称的函数,并将其返回。下面我们将结合这两种方法来进行演示,演示代码如下

    import shutil
    
    shutil.copytree("jove_test_module", "jove_test_module_copytree",ignore=shutil.ignore_patterns("*.md","top*"))

    代码效果如下:

    八、rmtree()

            递归的去删除整个目录,语法如下

    shutil.rmtree(path, ignore_errors=False, οnerrοr=None)

    参数说明:

  • path:要删除的目录路径。
  • ignore_errors:可选参数,如果设置为 True,则忽略错误,即使目录不存在也不会抛出异常。默认值为 False
  • onerror:可选参数,用于设置错误处理函数。默认值为 None,表示如果出现错误,则抛出异常
  • 演示代码如下

    import shutil
    
    # 删除目录及其下的所有文件和子目录
    shutil.rmtree('/path/to/directory')
    
    # 忽略错误,即使目录不存在也不会抛出异常
    shutil.rmtree('/path/to/directory', ignore_errors=True)
    
    # 自定义错误处理函数
    def handle_error(func, path, exc_info):
        print(f"Error: {path} - {exc_info[1]}")
    
    shutil.rmtree('/path/to/directory', onerror=handle_error)

    注意:使用该函数要格外小心,因为它会删除整个目录,包括目录下的所有文件和子目录,且无法恢复!

    九、move()

            递归的去移动整个目录,它类似 Linux 中的 mv 命令,其实就是重命名,语法如下

    shutil.move(src, dst, copy_function=shutil.copy2)

    参数说明:

  • src:源目录的路径
  • dst:目标目录的路径
  • copy_function:可选参数,用于指定在移动文件时是否保留源文件的状态信息(例如文件的访问时间和权限)。默认为 shutil.copy2()
  • 演示代码如下

    import shutil
    
    # 移动文件
    shutil.move('file.txt', 'new_location/file.txt')    # 目标路径的目录要先创建好
    
    # 移动目录
    shutil.move('dir', 'new_location/dir')    # 目标路径的目录要先创建好

    代码效果如下:

    十、make_archive()

            创建压缩包并返回文件路径,例如:zip、tar,语法如下

    shutil.make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, dry_run=False, owner=None, group=None, logger=None)

    参数说明:

  • base_name:要创建的压缩文件的名称,包括文件路径和文件名,只是文件名时,则保存至当前目录,否则保存至指定路径。例如 data_bak ——> 保存至当前路径;C:/tmp/data_bak ——> 保存至 C:/tmp/
  • format:压缩文件的格式。可以是"zip"、"tar"、"gztar"、"bztar"或"xztar"
  • root_dir:要压缩的文件夹的路径。如果未指定,则默认为当前工作目录
  • base_dir:要包含在压缩文件中的目录的路径。如果未指定,则默认为 root_dir
  • verbose:控制输出的详细程度。0表示没有输出,1表示输出压缩过程的摘要信息,2表示输出详细的压缩过程信息
  • dry_run:如果设置为 True,则不会创建实际的压缩文件,而只是模拟压缩的过程
  • owner:压缩文件的所有者。该参数仅在 Unix/Linux 系统上有效
  • group:压缩文件的所属组。该参数仅在 Unix/Linux 系统上有效
  • logger:用于记录日志的对象,通常是 logging.Logger 对象
  •  演示代码如下

    import shutil
    
    # 将  .\new_location 下的文件打包放置当前程序目录
    ret = shutil.make_archive("data_bak","gztar",root_dir=r".\new_location")
    
    # 将 .\new_location 下的文件打包放置 C:\Users\Administrator\Desktop\tmp\ 目录
    ret = shutil.make_archive(r"C:\Users\Administrator\Desktop\tmp\data_bak","gztar",root_dir=r".\new_location")

    代码效果如下:

            其实 shutil 对压缩包的处理是调用 zipfile 和 tarfile 两个模块来进行的,下面我们也来讲一讲这两个模块的用法。

    zipfile 压缩和解压缩:

    import zipfile
    
    # 压缩
    z = zipfile.ZipFile(r"C:\Users\Administrator\Desktop\test_compress.zip","w")
    z.write("jove_test_module")
    z.write("game.json")
    z.close()
    
    # 解压缩
    z = zipfile.ZipFile(r"C:\Users\Administrator\Desktop\test_compress.zip","r")
    z.extractall(path=r"C:\Users\Administrator\Desktop\tmp")
    z.close()

    代码效果如下:

            但是会发现一个问题,ZipFile 并没有把整个目录都拷贝过来,只是把最表层的目录拷贝了过去而已,如图所示

            这与我们的设想不太一样,这个时候我们可以使用 os 模块中的 walk() 方法,来把整个目录拷贝过去,我们先来看看 os.walk() 到底如何使用先,代码如下

    import os
    
    for i in os.walk("./jove_test_module"):
        print(i)

    代码输出如下:

            输出当中每一次输出都是一个元组,而元组包含三个元素,第一个是 root 目录,第二个是 root 目录下的目录,第三个是 root 目录下的文件。分析完输出的数据后就可以根据 os.walk() 来编写程序了,代码如下

    import os
    import zipfile
    
    z = zipfile.ZipFile(r'C:\Users\Administrator\Desktop\test_compress.zip','w')
    
    filelist = []
    for root, dirs, files in os.walk(r"jove_test_module"):
        if not files:
            if not dirs:
                filelist.append(root)
        else:
            for name in files:
                filelist.append(os.path.join(root,name))
    
    for file in filelist:
        print(file)
        z.write(file)
    
    z.close()

    代码效果与输出如下:

    tarfile 压缩和解压缩:

            将目标文件打包为 tar 包,而且打包为 tar 包是只打包不压缩的,代码如下

    import tarfile
    
    
    # 压缩
    t = tarfile.open(r'C:\Users\Administrator\Desktop\test_compress.tar','w')
    t.add("jove_test_module")
    t.add(r"jove_test_module\setup.py")
    t.add("game.json")
    t.close()
    
    # 解压缩
    t = tarfile.open(r'C:\Users\Administrator\Desktop\test_compress.tar','r')
    t.extractall("./tmp",filter="fully_trusted")  # 解压整个文件
    t.extract("jove_test_module/setup.py","./tmp/jove_module",filter="fully_trusted")   # 解压单个文件
    t.close()

    代码效果如下:

    注意:tarfile 模块存在限制,为了避免解压时会出现覆盖其他文件的错误操作,这就导致了可能会出现一下报错:DeprecationWarning: Python 3.14 will, by default, filter extracted tar archives and reject files or modify their metadata. Use the filter argument to control this behavior.
      t.extractall("./tmp"),这就是由于 tarfile 的安全限制导致的,这个时候我们需要使用  filter="fully_trusted" 来放开限制

    filter 参数详细可参见官方文档:tarfile — Read and write tar archive files — Python 3.12.7 documentation

    作者:JoveZou

    物联沃分享整理
    物联沃-IOTWORD物联网 » [Python学习日记-52] Python 中的 copy 模块 —— shutil

    发表回复