python模块之shutil

bnmcvzx 2019-06-28

shutil模块提供了一些针对文件和目录的高级操作,主要是拷贝、移动。对于单个文件的操作,还可参考os模块

Warning:即使是高级别的拷贝函数(shutil.copy(),shutil.copy2())也不能拷贝所有的文件元数据。意思是:在POSIX系统中,文件所有者、属组以及ACL信息会丢失。在Windows平台上,文件所有者,ACL以及ADS(供选数据流)不会被复制。On Mac OS, the resource fork and other metadata are not used. This means that resources will be lost and file type and creator codes will not be correct.

目录及文件操作

shutil.copyfileobj(fsrc, fdst[, length])

复制file-like对象fsrc的内容到fdst,如果fdst不存在则自动创建。length表示缓冲大小,如果是负数表示直接复制,不循环遍历块中的源数据。数据默认按块读取(16 * 1024)避免不可控的内存消耗。

import shutil

shutil.copyfileobj(open("fsrc.txt", "rb"), open("fdst.txt", "wb"))

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

复制文件src的内容到dst并返回dst,如果dst不存在则自动创建。src和dst是字符串类型的路径名,如果src和dst指向同一个文件,抛出SameFileError异常。

目标位置必须是可写的,否则将抛出OSError异常(实际抛出的是PermissionError)。如果dst已经存在,则直接覆盖。特殊文件比如块设备、字符设备、管道不能使用此函数复制。

如果follow_symlinks为False且src是软链接,将创建一个新的软链接替代拷贝行为

shutil.copyfile("src.txt", "dst.txt")

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

复制文件src的权限位(permission bits)到dst,src和dst是字符串类型的路径名。如果follow_symlinks为False且src和dst都是软链接,将修改dst软链接文件而非源文件的权限。

此函数并非所有平台可用,如果它不能修改本地平台的软链接但又执行了相关操作,将不做任何操作直接返回None

# 修改前
-rw-r--r--. 1 root  root    96 10月 11 12:26 aa.txt
-rw-------. 1 root  root  1362 9月   6 22:07 anaconda-ks.cfg
-rw-------. 1 admin admin    0 10月 11 12:53 bb.txt
lrwxrwxrwx. 1 root  root    21 10月 11 12:58 cc.txt -> /root/anaconda-ks.cfg

>>> shutil.copymode("aa.txt", "bb.txt")
>>> shutil.copymode("aa.txt", "cc.txt")

# 修改后
-rw-r--r--. 1 root  root    96 10月 11 12:26 aa.txt
-rw-r--r--. 1 root  root  1362 9月   6 22:07 anaconda-ks.cfg
-rw-r--r--. 1 admin admin    0 10月 11 12:53 bb.txt
lrwxrwxrwx. 1 root  root    21 10月 11 12:58 cc.txt -> /root/anaconda-ks.cfg

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

复制src的权限位、最后访问时间、最后修改时间以及标志(flag)到dst,src和dst是字符串类型的路径名,可以是文件或目录。在Linux平台上还会复制扩展属性。

如果follow_symlinks为False,且src和dst都是软链接,此函数直接操作软链接而非源文件(目录)。

Note:并非所有平台都能检查和修改软链接,python能告诉用户本地平台可使用哪些功能。

  • 如果os.chmod in os.supports_follow_symlinks为True,copystat()可以修改软链接的权限位
  • 如果os.utime in os.supports_follow_symlinks为True,copystat()可以修改软链接的最后访问时间和最后修改时间
  • 如果os.chflags in os.supports_follow_symlinks为True,copystat()可以修改软链接的flag

copystat()总是能成功执行,即使是在某些它的部分或全部功能不可用的平台上修改软链接,它将最大限度地拷贝它能拷贝的所有信息。

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

复制文件src的内容和权限位到dst,dst可以是文件或目录,如果是文件,函数的返回值就是dst,如果是目录,函数的返回值就是src的文件名与dst的路径拼接。src和dst都是字符串类型,如果dst指向一个目录,则创建与src同名(basename)的新文件。

如果follow_symlinks为False,且src是软链接,dst将作为软链接创建;如果follow_symlinks为True,src为软链接,则实际拷贝的是src指向的源文件。

copy()使用copymode()拷贝权限位,使用copyfile()拷贝文件内容

>>> shutil.copy("src.txt", "dst.txt")
'dst.txt'
>>> shutil.copy("src.txt", "/tmp/")
'/tmp/src.txt'
>>> shutil.copy("src.txt", "/Dota2/")     # 传入一个不存在的目录
Traceback (most recent call last):
  ...
IsADirectoryError: [Errno 21] Is a directory: '/Dota2/'

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

除了还会保留src的所有元数据(如创建时间、修改时间等),其他与copy()相同。

当follow_symlinks为False且src为软链接时,dst将作为软链接被创建并拷贝src的所有元数据到dst。

copy2()使用copystat()拷贝元数据,使用copyfile()拷贝文件内容

shutil.ignore_patterns(*patterns)

创建并返回一个函数,可传递到copytree()中作为ignore参数的值,忽略满足匹配模式的文件和目录

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

递归复制以src为根目录的整个目录树,返回目标目录dst。dst必须是不存在的目录,它和它不存在的父目录都将被创建。使用copystat()复制目录元数据,使用copy2()复制文件内容和元数据

  • symlinks:如果值为True,拷贝src目录树中的软链接和软链接的元数据到dst目录树;如果值为False,拷贝src目录树中软链接指向的源文件(目录)的内容和元数据到dst目录树(需要注意的是,拷贝后的文件或目录的名称使用的是软链接的名称而非源文件或目录的名称),倘若源文件不存在将导致异常,异常信息保存在最终抛出的Error异常中。
  • ignore_dangling_symlinks:如果值为True,可以忽略symlinks参数为False时由于源文件不存在产生的异常。对于不支持os.symlink()的平台,此参数无任何影响
  • ignore:必须是一个可调用对象,接收src目录树下的目录和使用os.listdir()返回的该目录下的文件、目录列表。由于copytree()是递归调用的,因此src目录树下每个被复制的目录都会调用一次ignore。
  • copy_function:必须是一个可调用对象,用于复制文件

原始目录结构:

[root@localhost Shutil]# pwd
/root/Shutil
[root@localhost Shutil]# ll -R
.:
总用量 0
drwxr-xr-x. 3 root root 70 10月 12 10:48 src
drwxr-xr-x. 3 root root 33 10月 12 10:47 symlink

./src:
总用量 0
-rw-r--r--. 1 root root  0 10月 12 10:39 aa.txt
drwxr-xr-x. 2 root root  6 10月 12 10:40 dddir
lrwxrwxrwx. 1 root root 27 10月 12 10:48 ssdir_link -> /root/Shutil/symlink/ssdir/
lrwxrwxrwx. 1 root root 27 10月 12 10:48 ss_link.txt -> /root/Shutil/symlink/ss.txt

./src/dddir:
总用量 0

./symlink:
总用量 0
drwxr-xr-x. 2 root root 6 10月 12 10:57 ssdir
-rw-r--r--. 1 root root 0 10月 12 10:46 ss.txt

./symlink/ssdir:
总用量 0

shutil.copytree("/root/Shutil/src/", "/root/Shutil/dst/", symlinks=False)的执行结果:

[root@localhost Shutil]# ll dst/
总用量 0
-rw-r--r--. 1 root root 0 10月 12 10:39 aa.txt
drwxr-xr-x. 2 root root 6 10月 12 10:40 dddir
drwxr-xr-x. 2 root root 6 10月 12 10:57 ssdir_link
-rw-r--r--. 1 root root 0 10月 12 10:46 ss_link.txt

shutil.copytree("/root/Shutil/src/", "/root/Shutil/dst/", symlinks=True)的执行结果:

[root@localhost Shutil]# ll dst/
总用量 0
-rw-r--r--. 1 root root  0 10月 12 10:39 aa.txt
drwxr-xr-x. 2 root root  6 10月 12 10:40 dddir
lrwxrwxrwx. 1 root root 27 10月 12 10:48 ssdir_link -> /root/Shutil/symlink/ssdir/
lrwxrwxrwx. 1 root root 27 10月 12 10:48 ss_link.txt -> /root/Shutil/symlink/ss.txt

shutil.rmtree(path, ignore_errors=False, onerror=None)

删除目录。path必须指代一个目录(但不能是目录的软链接)。

  • ignore_errors:如果为True,忽略目录删除失败导致的异常,否则该异常将由onerror参数指定的函数处理。如果onerror为None,该异常被抛出
  • onerror:必须是一个接收三个参数(function, path, excinfo)的可调用对象。

Note:在支持基于文件描述符的目录访问函数的平台上,默认使用抗软链接攻击(symlink attack resistant)的rmtree()版本;其他平台上的rmtree()的实现易遭受软链接攻击。可以用rmtree.avoids_symlink_attacks查看当前平台的rmtree()方法能否抵抗软链接攻击

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

移动文件或目录到目标位置。

  • 如果目标位置dst是一个存在的[软链接]目录,将src(文件或目录)移动到dst路径下
# before
[root@localhost shutil_move]# pwd
/root/shutil_move
[root@localhost shutil_move]# ll
总用量 0
drwxr-xr-x. 2 root root  6 10月 12 14:41 dst_dir
drwxr-xr-x. 2 root root 20 10月 12 14:39 src

>>> shutil.move("/root/shutil_move/src/", "/root/shutil_move/dst_dir/")
'/root/shutil_move/dst_dir/src'

# after
[root@localhost shutil_move]# ll -R
.:
总用量 0
drwxr-xr-x. 3 root root 17 10月 12 14:43 dst_dir

./dst_dir:
总用量 0
drwxr-xr-x. 2 root root 6 10月 12 14:45 src

./dst_dir/src:
总用量 0
  • 如果目标位置dst是一个不存在的目录,创建dst及其不存在的父级目录,将src(文件)移动到dst路径下或将src(目录)移动并重命名为dst
# before
[root@localhost shutil_move]# pwd
/root/shutil_move
[root@localhost shutil_move]# ll
总用量 0
drwxr-xr-x. 2 root root 6 10月 12 14:55 src
[root@localhost shutil_move]# ll /tmp/
总用量 0

>>> shutil.move("/root/shutil_move/src/", "/tmp/a/b/dst/")
'/tmp/a/b/dst/'

# after
[root@localhost shutil_move]# ll
总用量 0
[root@localhost shutil_move]# ll -R /tmp/
/tmp/:
总用量 0
drwxr-xr-x. 3 root root 15 10月 12 14:57 a

/tmp/a:
总用量 0
drwxr-xr-x. 3 root root 17 10月 12 14:57 b

/tmp/a/b:
总用量 0
drwxr-xr-x. 2 root root 6 10月 12 14:55 dst

/tmp/a/b/dst:
总用量 0
  • 如果dst非目录,且dst存在或其上级目录存在

    • src是普通文件,dst是普通文件或软链接文件,移动src并重命名为dst。原始dst如果存在则被直接覆盖
    • src是软链接文件,dst是普通文件或软链接文件,移动src并重命名为dst,指向的真实文件为src之前指向的源文件。原始dst如果存在则被直接覆盖

shutil.disk_usage(path)

以命名元组的方式(total, used, free)返回指定path的磁盘使用数据,单位为byte。在Windows平台,path必须指代目录;在Unix平台path可以是目录或文件

shutil.chown(path, user=None, group=None)

改变指定path的所有者和属组。

user和group参数,可以是系统上的用户名/组名或uid/gid。至少需要传递其中一个参数

shutil.which(cmd, mode=os.F_OK | os.X_OK, path=None)

返回cmd调用的可执行文件路径,没有返回None。

  • mode:传递给os.access()的权限掩码(permission mask),默认值为os.F_OK | os.X_OK,用于判断文件是否存在或可执行。
  • path:cmd的查找路径。如果未指定,在os.environ的"PATH"key指代的路径查找,如果"PATH"不存在,使用os.defpath作为默认路径。

在Windows平台,不管使用默认path还是自定义的path,查找路径都将包含当前工作目录且处于最高优先级。此外,在查找cmd时,还会检查环境变量PATHEXT

>>> shutil.which("python3")
'D:\\Program Files\\Python36\\python3.EXE'

copytree示例

使用ignore_patterns()忽略拷贝后缀为.pyc的文件和tmp打头的文件或文件夹:

from shutil import copytree, ignore_patterns

copytree(source, destination, ignore=ignore_patterns('*.pyc', 'tmp*'))

使用ignore参数记录日志:

from shutil import copytree
import logging

def _logpath(path, names):
    logging.info('Working in %s', path)
    return []   # nothing will be ignored

copytree(source, destination, ignore=_logpath)

rmtree示例

import os, stat
import shutil

def remove_readonly(func, path, _):
    "Clear the readonly bit and reattempt the removal"
    os.chmod(path, stat.S_IWRITE)
    func(path)

shutil.rmtree(directory, onerror=remove_readonly)

归档操作

基于zipfile和tarfile模块提供创建和读取归档文件的功能。

shutil.make_archive(base_name, format[, root_dir[, base_dir[, verbose[, dry_run[, owner[, group[, logger]]]]]]])

创建归档文件,并返回归档文件的名称。

  • base_name:要创建的归档文件的名称(不包含扩展名),可以包含路径表示归档文件的目标位置
  • format:归档文件的格式(zip, tar, gztar, bztar, xztar)
  • root_dir:归档文件的根目录(默认当前目录)
  • base_dir:归档文件中所有文件和目录的前缀路径(默认当前目录)
  • dry_run:如果为True,不创建归档文件,但是将执行的操作记录在logger中
  • owner/group:归档文件中所有文件和目录的所属用户和组。如果format为"zip",owner和group的配置不生效
  • logger:通常使用logging.Logger对象
  • verbose:已弃用
# /root/demo/ 归档该目录下的文件和文件夹
# /tmp/shutil/ 生成的归档文件中的所有内容的前缀路径
>>> shutil.make_archive("/root/demo_zip/zipfile", "zip", "/root/demo/", "/tmp/shutil/")
'/root/demo_zip/zipfile.zip'
[root@localhost demo_zip]# unzip -l zipfile.zip 
Archive:  zipfile.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  10-15-2018 11:03   tmp/shutil/
        0  10-15-2018 11:03   tmp/shutil/aa.txt
        0  10-15-2018 11:03   tmp/shutil/bb.txt
        0  10-15-2018 11:03   tmp/shutil/cc.txt
---------                     -------
        0                     4 files

shutil.get_archive_formats()

返回支持的归档格式列表,列表中的每个元素是(name, description)形式的元组。
默认支持的格式如下:

  • zip:需要zlib模块支持
  • tar
  • gztar:需要zlib模块支持
  • bztar:需要bz2模块支持
  • xztar:需要lzma模块支持

用户可以通过register_archive_format()注册新的格式或者自定义已存在格式的归档行为

shutil.register_archive_format(name, function[, extra_args[, description]])

注册格式为name的归档器

shutil.unregister_archive_format(name)

从支持的归档格式中移除name

shutil.unpack_archive(filename[, extract_dir[, format]])

解压归档文件。

  • filename:归档文件名称
  • extract_dir:归档文件解压的目标位置。默认使用当前目录
  • format:使用指定格式的解压器解压归档文件,默认使用filename参数的扩展名。如果不存在对应的解压器,抛出ValueError异常

shutil.register_unpack_format(name, extensions, function[, extra_args[, description]])

注册格式为name的解压器

shutil.unregister_unpack_format(name)

从支持的解压格式中移除name

shutil.get_unpack_formats()

返回支持的解压格式列表,列表中的每个元素是(name, extensions, description)形式的元组。

查询终端大小

shutil.get_terminal_size(fallback=(columns, lines))

获取终端窗口的大小。

如果环境变量中定义了正整数的COLUMNSLINES,返回该大小。

如果COLUMNSLINES未定义(多数情况都是如此),返回调用os.get_terminal_size()查询连接到sys.__stdout__的终端的大小。

如果终端大小不能被查询(系统不支持或者未连接到终端),使用fallback参数提供的大小作为后备值,默认是(80, 24)

相关推荐

InJavaWeTrust / 0评论 2020-01-28