shutil:对文件进行高级操作,详细介绍 shutil 模块

shutil 模块介绍

shutil 模块是 Python 内置的对文件、目录、压缩文件进行高级操作的模块,该模块对文件的复制、删除和压缩等操作都提供了非常方便的支持。

在使用 shutil 模块时,不能复制所有文件的元数据。在 POSIX 平台上不能复制文件的所有者和组、以及访问控制表;在 macOS 中不能复制文件类型和创建者;在 Windows 中不能复制文件的所有者、访问控制列表和备用数据流。

shutil 模块是 Python 内置,直接导入即可使用。

chown:更改指定路径的所有者用户(组)

函数原型:shutil.chown(path, user=None, group=None),参数含义如下:

  • path:指定要操作的路径
  • user:指定所有者,可以是系统用户名或者 UID,如果用户不存在则报错 "没有此用户"
  • group:表示组

该方法只适用于 Unix 系统。

>>> import shutil
>>> import pwd  # 该模块需要在 Unix 上使用
>>> import os
>>> 
>>> uid = os.stat("/root").st_uid
>>> pwd.getpwuid(uid)
pwd.struct_passwd(pw_name='root', pw_passwd='x', pw_uid=0, pw_gid=0, pw_gecos='root', pw_dir='/root', pw_shell='/bin/bash')
>>> # 可以通过 shutil 进行调用,然后再进行查看

copy:复制文件或目录

copy 函数可以将一个文件复制为另一个文件(或另一个目录中)。

函数原型:shutil.copy(src, dst, *, follow_symlinks=True),参数含义如下:

  • src:表示文件路径的字符串,表示源文件(注意:必须是文件,如果是目录则报出权限错误)
  • dst:表示文件或目录路径的字符串,如果是一个已经存在的目录,那么会将 src 拷贝到该目录中;否则会创建相应的文件。
  • follow_symlinks:表示是否遵循符号链接,默认为 True。如果为 True 则赋值文件,为 False、并且 src 为软连接,则创建一个新的软连接

该函数会返回目标路径,即新创建的文件的路径。

import shutil

# 如果 test 存在并且是目录,那么会将 1.txt 拷贝到 test 中
shutil.copy("1.txt", "test")

# 如果 test 不存在,那么会创建一个名为 test 的文件,内容和 1.txt 一致
# 如果 test 存在并且不是目录,那么会把原来的文件覆盖掉(需要具备写权限,否则报错 PermissionError)
shutil.copy("1.txt", "test")

使用 copy 复制文件时,文件的元信息(创建时间、修改时间)不会被保留,相当于创建了新文件。如果要保留文件的元信息,需要使用 copy2 函数(和 copy 函数用法一致,区别就是前者不保留文件元信息、后者会保留)。

copyfile:复制文件内容

参数和 copy、copy2 完全一致,只不过 copyflle 的 dst 如果已存在,那么必须是文件。

# 如果 test 存在并且是目录,会报错:PermissionError: [Errno 13] Permission denied: 'test'
shutil.copyfile("1.txt", "test")

# 如果 test 不存在,那么会创建一个名为 test 的文件,内容和 1.txt 一致
# 如果 test 存在并且不是目录,那么会把原来的文件覆盖掉
shutil.copyfile("1.txt", "test")

比较简单,可以自己试一下,所以 copy 要比 copyfile 更高级一些。

使用 copyfile 同样需要写权限,并且 src 和 dst 不能是同一个文件,否则会报错:SameFileError。

比较简单,可以自己试一下,所以 copy 要比 copyfile 更高级一些。并且除了 copyfile,还有一个更加低级的 copyfileobj。copyfileobj 也是拷贝,接收三个参数:fsrc、fdst、length,前两个参数和 copy 类似,只不过 fsrc 和 fdst 都必须是打开的文件对象,从名字上也能看出。至于第三个参数 length 表示缓冲区,如果为负数表示直接复制,默认是 16 * 1024 字节。

# 这么写会报错:AttributeError: 'str' object has no attribute 'read'
# 因为接收的是文件对象,会调用 read 进行读取
shutil.copyfile("1.txt", "test")

所以 src 是一个读模式打开的文件,dst 是一个写模式打开的文件,然后将 src 拷贝到 dst 中,这个函数更加低级。我们可以使用 io.StringIO 演示一下:

import shutil
from io import StringIO

buf1 = StringIO()
buf2 = StringIO()

# buf1 里面写入一些内容
buf1.write("古明地觉")
# 调整指针,移到开头,否则读取不到内容
buf1.seek(0)
# 将 buf1 拷贝到 buf2 中
shutil.copyfileobj(buf1, buf2)
# 查看 buf2 中的内容
print(buf2.getvalue())  # 古明地觉

虽然 copyfileobj 比较低级,但是它的速度也更快。当复制大文件时,采用 copyfileobj 会更有效率,复制小文件则使用 copyfile 会更快一些。

copymode:复制权限位

参数和 copy 函数也完全相同,只不过它是将一个文件的权限复制给另一个文件。比如 A 文件是只读,那么复制给 B 之后 B 也是只读,但是 A 的内容不会复制给 B,因为 copymode 只是复制权限。

除了 copymode 还有一个 copystat,参数也是一样的,只不过它除了复制权限之外还复制最后访问时间、最后修改时间等元信息,可以自己试一下这两个函数。

copytree:递归复制整个目录树

copytree 方法可以递归复制整个目录,并返回目标目录的路径,语法格式如下:

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

参数含义如下:

  • src:表示路径的字符串,必须是一个已存在的目录,不能是文件
  • dst:表示路径的字符串,必须是一个不存在的目录,否则报错:FileExistsError
  • symlinks:是否遵循符号链接,默认为 True。如果为 True,表示复制文件,如果为 False,那么当 src 为软连接、则创建一个新的软连接
  • ignore:可选参数,表示一个可调用对象,该可调用对象接收两个参数,第一个参数是要复制的目录名,第二个参数是要复制的目录里面的所有目录或文件的名称(不递归显示子目录中,只显示内部一层)
  • copy_function:从默认值可以看出,表示拷贝函数,这里采用的是 copy2,会将文件的元信息也保留
  • ignore_dangling_symlinks:是否忽略 symlinks,如果值为 True 则忽略,值为 False,那么当文件不存在时则产生异常。对于不支持 os.symlink() 的平台,此参数无任何影响

比较简单,可以自己尝试一下。

disk_usage:获取磁盘的使用情况

该函数接收一个参数 path 表示路径,会自动获取改路径所在磁盘的使用情况:总空间、已使用空间和空闲空间,以字节为单位。

在 Windows 中 path 必须是一个目录,在 Unix 中可以是目录也可以是文件。

import shutil

disk_F = shutil.disk_usage("F:")
print(disk_F)  # usage(total=1000202039296, used=447226023936, free=552976015360)
print(disk_F.total / 1024 / 1024 / 1024)  # 931.5107383728027
print(disk_F.used / 1024 / 1024 / 1024)  # 416.51169204711914
print(disk_F.free / 1024 / 1024 / 1024)  # 514.9990463256836

get_archive_formats:获取支持的压缩格式

get_archive_formats 函数可以返回当前系统支持的压缩格式列表:

import shutil
from pprint import pprint

pprint(shutil.get_archive_formats())
"""
[('bztar', "bzip2'ed tar-file"),
 ('gztar', "gzip'ed tar-file"),
 ('tar', 'uncompressed tar file'),
 ('xztar', "xz'ed tar-file"),
 ('zip', 'ZIP file')]
"""

既然有压缩,那么就有解压缩,get_unpack_formats 函数可以返回当前系统支持的解压缩格式列表:

import shutil
from pprint import pprint

pprint(shutil.get_unpack_formats()) 
"""
[('bztar', ['.tar.bz2', '.tbz2'], "bzip2'ed tar-file"),
 ('gztar', ['.tar.gz', '.tgz'], "gzip'ed tar-file"),
 ('tar', ['.tar'], 'uncompressed tar file'),
 ('xztar', ['.tar.xz', '.txz'], "xz'ed tar-file"),
 ('zip', ['.zip'], 'ZIP file')]
"""

get_terminal_size:获取终端窗口的大小

get_terminal_size 函数可以获取终端窗口的大小,语法格式如下:

shutil.get_terminal_size(fallback=(80, 24))

如果系统不支持查询,或者未连接到终端,那么返回 (80, 24),表示列数和行数。

import shutil

print(shutil.get_terminal_size())  # os.terminal_size(columns=80, lines=24)

ignore_patterns:提供 glob 通配符功能

ignore_patterns 是一个工厂函数,用于为 shutil.copytree 的 ignore 参数提供值,该值在复制的过程中会被忽略。比如:我们在使用 shutil.copytree 的时候不想复制 .txt 结尾和 bak 开头的文件,那么就可以这么做:

import shutil

shutil.copytree("test1", "test2", ignore=shutil.ignore_patterns("*.go", "bak*"))

make_archive:创建压缩文件并返回文件路径

通过 make_archive 可以创建压缩文件,语法格式如下:

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

参数含义如下:

  • base_name:表示压缩包的文件名(不包含扩展名),也可以包含路径,如果只写文件名则保存到当前目录,否则保存到指定路径
  • format:表示压缩包格式,如 zip、tar、bztar、gztar 等
  • root_dir:表示将要被压缩的目录路径,默认是当前目录
  • base_dir:表示开始压缩的目录,即压缩文件中所有文件和目录的公共前缀,默认为当前目录
  • verbose:已弃用
  • dry_run:表示是否创建存档,如果 dry_run 为 True,则不会创建存档,但会将执行的操作记录到 logger
  • owner:可选参数,用于指定用户,默认为当前用户
  • group:可选参数,用于指定组,默认为当前组
  • logger:用于记录日志,通常为 logging.Logger 对象

make_archive 函数依赖于 zipfile 和 tarfile 模块。

import shutil

shutil.make_archive("tmp", "zip", root_dir=r"F:\动漫\缘之空")

之后会在当前目录中出现一个 tmp.zip,目录 r"F:\动漫\缘之空" 里面的所有内容都会被压缩到里面。

有压缩,那么自然有解压缩,shutil.unpack_archive(filename, extract_dir=None, format=None):

  • filename:解压缩文件的路径
  • extract_dir:解压到哪个目录,未指定则解压到当前目录
  • format:表示压缩包的格式,如:zip、bztar、gztar 等等,如果没有提供那么则根据压缩文件扩展名进行推断

该方法同样依赖于 zipfile 和 tarfile 两个模块。

move:移动文件和目录

move 方法用于将文件或目录移动到目标目录,如果移动到了不同的文件系统中,那么移动将会变成复制。参数如下:

  • src:无需解释
  • dst:无需解释
  • copy_function:默认是 copy2

举个栗子:

import shutil
# 将 F 盘的 code1 目录移动到 code2 目录
shutil.move(r"F:\code1", r"F:\code2")

rmtree:删除整个目录树

rmtree 函数用于删除整个目录树,参数如下:

  • path:表示路径的字符串,必须是一个目录,不能是文件
  • ignore_errors:默认为 False,表示是否忽略删除中出现的错误。如果为 True 表示忽略、为 False 表示不忽略
  • onerror:一个错误处理函数,接收三个参数:func(引发异常的函数)、path(传递到函数中的路径)、excinfo(返回的异常信息)。如果 onerror 被省略,那么当发生错误时会给出提示
import shutil

shutil.rmtree(r"F:\code1")

which:获取可执行文件的路径

我们在终端中输入 python 的时候会自动进入交互式解释器中,这是因为在环境变量中配置了 python 解释器的路径,而通过 which 函数可以获取相应的路径。该函数接收参数如下:

  • cmd:相关命令
  • mode:用于指定需要传递的权限掩码,默认为 os.F_OK | os.X_OK,表示测试路径是否存在、并且是否可执行
  • path,默认为 None,表示查找 cmd 命令的路径。如果不指定则在环境变量中查找,指定了则在指定的路径参数查找。但是注意:不管指没指定,当前目录始终会被添加到搜索路径中
import shutil

print(shutil.which("python"))  # C:\python38\python.EXE
print(shutil.which("go"))  # C:\Go\bin\go.EXE
print(shutil.which("lua"))  # C:\lua\lua.EXE
print(shutil.which("gcc"))  # C:\MinGW\bin\gcc.EXE
print(shutil.which("xxxxx"))  # None

小结

以上就是 shutil 的一些用法,可以说在对文件处理方面做了很多工作,在工作中不妨多使用以下。尤其是在涉及文件拷贝的时候,真的非常方便。

posted @ 2019-07-21 14:14  古明地盆  阅读(1647)  评论(0编辑  收藏  举报