Python shutil.md

shutil

shutil模块包括高级文件操作,例如复制和归档。

Copying Files

shutil.copyfileobj(fsrc, fdst[, length]):将类似文件的对象fsrc的内容复制到类似文件的对象fdst。整数length(如果给出)是缓冲区大小。具体地,负的长度值意味着复制数据而不使块中的源数据循环;默认情况下,以块为单位读取数据,以避免不受控制的内存消耗。请注意,如果fsrc对象的当前文件位置不为0,则只会复制当前文件位置到文件末尾的内容。
shutil.copyfile(src, dst, *, follow_symlinks=True):将名为src的文件的内容(无元数据)复制到名为dst的文件,然后返回dst。src和dst是以字符串形式给出的路径名称。dst必须是完整的目标文件名;请查看shutil.copy()以获取接受目标目录路径的副本。如果src和dst指定相同的文件,则会引发SameFileError。目标位置必须可写;否则,将引发OSError异常。如果dst已经存在,它将被替换。使用此功能无法复制特殊文件,例如字符或块设备和管道。如果follow_symlinks为假且src是符号链接,则将创建一个新的符号链接,而不是复制src指向的文件。
shutil.copy(src, dst, *, follow_symlinks=True):将文件src复制到文件或目录dst。src和dst应为字符串。如果dst指定目录,则文件将使用src的基本文件名复制到dst中。返回新创建的文件的路径。如果follow_symlinks为false,并且src是符号链接,则dst将创建为符号链接。如果follow_symlinks为true且src是符号链接,则dst将是文件的副本src 。copy()复制文件数据和文件的权限模式(请参阅os.chmod())。其他元数据(如文件的创建和修改时间)不会保留。要保留原始文件的所有文件元数据,请改用copy2()。
shutil.copy2(src, dst, *, follow_symlinks=True):与copy()相同,但copy2()也尝试保留所有文件元数据。当follow_symlinks为false,且src是符号链接时,copy2()尝试从src t5 >符号链接到新创建的dst符号链接。但是,此功能不适用于所有平台。在某些或所有功能不可用的平台上,copy2()将保留其可用的所有元数据; copy2()从不会返回失败。copy2()使用copystat()复制文件元数据。有关修改符号链接元数据的平台支持的详细信息,请参阅copystat()。

举例
copyfile()将源文件内容完全复制给目标文件. 如果没有写入目标文件的权限, 会引起IOError. 由于该函数是为了读取文件内容而打开此输入文件, 而不管它的类型是什么, 特殊类型的文件使用copyfile()是不能拷贝的, 比如管道文件.

import glob
import shutil

print('BEFORE:', glob.glob('shutil_copyfile.*'))
shutil.copyfile('shutil_copyfile.py', 'shutil_copyfile.py.copy')
print('AFTER:', glob.glob('shutil_copyfile.*'))

注意:shutil_copyfile.py是需要存在并且访问路径正确。输出如下:

# python shutil_study.py
BEFORE: ['shutil_copyfile.py']
AFTER: ['shutil_copyfile.py', 'shutil_copyfile.py.copy']

copyfile()底层调用了copyfileobj()函数. 文件名参数传递给copyfile()后, 进而将此文件句柄传递给copyfileobj(). 第三个可选参数是一个缓冲区长度, 以块读入(默认情况下, 一次性读取整个文件).

import shutil
import io
import sys
import os

class VerboseStringIO(io.StringIO):
    def read(self, n = -1):
        next = io.StringIO.read(self, n)
        print('read({}) got {} bytes'.format(n, len(next)))
        return next

lorem_ipsum = '''Lorem ipsum dolor sit amet, consectetuer
adipiscing elit.  Vestibulum aliquam mollis dolor. Donec
vulputate nunc ut diam. Ut rutrum mi vel sem. Vestibulum
ante ipsum.'''

print('Default:')
input = VerboseStringIO(lorem_ipsum)
output = io.StringIO()
shutil.copyfileobj(input, output)

print()

print('All at once:')
input = VerboseStringIO(lorem_ipsum)
output = io.StringIO()
shutil.copyfileobj(input, output, -1)

print()

print('Blocks of 256:')
input = VerboseStringIO(lorem_ipsum)
output = io.StringIO()
shutil.copyfileobj(input, output, 256)

默认行为是使用大型块读取数据。使用-1读取一次或另一个正整数的所有输入,可以设置一个特定的块大小。这个例子使用了几个不同的块大小来显示效果。

Default:
read(16384) got 166 bytes
read(16384) got 0 bytes

All at once:
read(-1) got 166 bytes
read(-1) got 0 bytes

Blocks of 256:
read(256) got 166 bytes
read(256) got 0 bytes

copy()函数类似于Unix命令cp. 如果目标参数是一个目录而不是一个文件, 那么在这个目录中复制一个源文件副本(它与源文件同名). 文件的权限也随之复制.

import glob
import os
import shutil

os.mkdir('example')
print('BEFORE:', glob.glob('example/*'))

shutil.copy('shutil_copy.py', 'example')

print('AFTER :', glob.glob('example/*'))

文件的权限和内容一起复制。输出:

# python shutil_study.py
BEFORE: []
AFTER : ['example/shutil_copy.py']

copy2()函数类似于copy(), 但是它将一些元信息, 如文件最后一次被读取时间和修改时间等, 也复制至新文件中.

import os
import shutil
import time

def show_file_info(filename):
    stat_info = os.stat(filename)
    print('  Mode    :', oct(stat_info.st_mode))
    print('  Created :', time.ctime(stat_info.st_ctime))
    print('  Accessed:', time.ctime(stat_info.st_atime))
    print('  Modified:', time.ctime(stat_info.st_mtime))

os.mkdir('example')
print('SOURCE:')
show_file_info('shutil_copy2.py')

shutil.copy2('shutil_copy2.py', 'example')

print('DEST:')
show_file_info('example/shutil_copy2.py')

新文件具有与旧版本相同的特性。

# python shutil_study.py
SOURCE:
  Mode    : 0o100644
  Created : Mon Jul 10 00:03:16 2017
  Accessed: Mon Jul 10 00:03:16 2017
  Modified: Mon Jul 10 00:03:16 2017
DEST:
  Mode    : 0o100644
  Created : Mon Jul 10 00:03:26 2017
  Accessed: Mon Jul 10 00:03:26 2017
  Modified: Mon Jul 10 00:03:16 2017

Copying File Metadata

shutil.copymode(src, dst, *, follow_symlinks=True):将权限位从src复制到dst。文件内容,所有者和组不受影响。src和dst是以字符串形式给出的路径名称。
shutil.copystat(src, dst, *, follow_symlinks=True):将权限位,最后访问时间,上次修改时间和标志从src复制到dst。在Linux上,copystat()也会尽可能复制“扩展属性”。文件内容,所有者和组不受影响。src和dst是以字符串形式给出的路径名称。

举例
默认情况下, 在Unix下, 一个新创建的文件的权限会根据当前用户的umask值来设置. 把一个文件的权限复制给另一个文件, 可以使用copymode()函数.

import os
import shutil
import subprocess

with open('file_to_change.txt', 'wt') as f:
    f.write('content')
os.chmod('file_to_change.txt', 0o444)

print('BEFORE:', oct(os.stat('file_to_change.txt').st_mode))

shutil.copymode('shutil_copymode.py', 'file_to_change.txt')

print('AFTER :', oct(os.stat('file_to_change.txt').st_mode))

这个示例脚本创建一个要修改的文件,然后使用copymode()将脚本的权限复制到示例文件中。

# python  shutil_study.py 
BEFORE: 0o100444
AFTER : 0o100644

复制文件的其他元信息(权限, 最后读取时间, 最后修改时间)可以使用copystat().

import os
import shutil
import time

def show_file_info(filename):
    stat_info = os.stat(filename)
    print('  Mode    :', oct(stat_info.st_mode))
    print('  Created :', time.ctime(stat_info.st_ctime))
    print('  Accessed:', time.ctime(stat_info.st_atime))
    print('  Modified:', time.ctime(stat_info.st_mtime))

with open('file_to_change.txt', 'wt') as f:
    f.write('content')
os.chmod('file_to_change.txt', 0o444)

print('BEFORE:')
show_file_info('file_to_change.txt')

shutil.copystat('shutil_copystat.py', 'file_to_change.txt')

print('AFTER:')
show_file_info('file_to_change.txt')

只有与该文件相关联的权限和日期被复制到copystat()中。

# python shutil_study.py 
BEFORE:
  Mode    : 0o100444
  Created : Mon Jul 10 00:20:02 2017
  Accessed: Mon Jul 10 00:20:02 2017
  Modified: Mon Jul 10 00:20:02 2017
AFTER:
  Mode    : 0o100644
  Created : Mon Jul 10 00:20:02 2017
  Accessed: Mon Jul 10 00:12:15 2017
  Modified: Mon Jul 10 00:12:15 2017

Working With Directory Trees

shutil.ignore_patterns(*patterns):此工厂函数创建一个函数,可用作copytree()的忽略参数的可调用函数,忽略与glob类型模式。
shutil.copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, ignore_dangling_symlinks=False):递归地复制以src为根的整个目录树,返回目标目录。由dst命名的目标目录不能已经存在;它将被创建以及缺少父目录。使用copystat()复制目录的权限和时间,使用shutil.copy2()复制单个文件。如果符号链接为真,则源树中的符号链接在新树中表示为符号链接,并且原始链接的元数据将被复制到平台允许的范围内;如果为false或省略,链接文件的内容和元数据将复制到新树。当符号链接为false时,如果符号链接指向的文件不存在,则在Error异常中引发的错误列表中将添加一个异常复制过程。如果要冻结此异常,可以将可选的ignore_dangling_symlinks标志设置为true。请注意,此选项对不支持os.symlink()的平台没有影响。
shutil.rmtree(path, ignore_errors=False, onerror=None):删除整个目录树; 路径必须指向目录(而不是指向目录的符号链接)。如果ignore_errors为true,则删除失败导致的错误将被忽略;如果为false或省略,则通过调用onerror指定的处理程序处理这些错误,如果省略,则引发异常。
shutil.move(src, dst, copy_function=copy2):递归地将文件或目录(src)移动到另一个位置(dst),并返回目标。如果目标是现有目录,则src在该目录中移动。如果目标已经存在,但不是目录,根据os.rename()语义,它可能会被覆盖。如果目标位于当前文件系统上,则使用os.rename()。否则,使用copy_function将src复制到dst,然后删除。在符号链接的情况下,指向src的目标的新符号链接将在dst中创建或创建,src将被删除。如果给出copy_function,它必须是一个可调用,它接受两个参数src和dst,并且将用于复制src 到dest如果os.rename()不能使用。如果源是目录,则调用copytree(),将其传递给copy_function()。默认的copy_function是copy2()。使用copy()作为copy_function允许移动成功,当不可能也复制元数据,而不复制任何元数据。

举例
shutil模块包含3个操作目录树的函数. 使用copytree()来复制目录, 它会递归复制整个目录结构. 目标目录必须不存在. 其中, symlinks参数控制符号链接是否作为链接或文件被复制, 默认是将其内容复制成一个新文件. 如果此选项为true, 新的链接会在目标目录树中创建.

import glob
import pprint
import shutil

print('BEFORE:')
pprint.pprint(glob.glob('/tmp/example/*'))

shutil.copytree('../shutil', '/tmp/example')

print('\nAFTER:')
pprint.pprint(glob.glob('/tmp/example/*'))

符号链接参数控制符号链接是否被复制为链接或文件。缺省情况是将内容复制到新文件中。如果选项是true,则在目标树中创建新的符号链接。

# python shutil_study.py 
BEFORE:
[]

AFTER:
['/tmp/example/shutil_copymode.py',
 '/tmp/example/testdata.csv',
 '/tmp/example/example',
 '/tmp/example/testout.csv',
 '/tmp/example/csv_study.py',
 '/tmp/example/unicode_chars',
 '/tmp/example/file_to_change.txt',
 '/tmp/example/shutil_copyfile.py',
 '/tmp/example/testdata.pipes',
 '/tmp/example/os_path_study.py',
 '/tmp/example/shutil_copy2.py',
 '/tmp/example/shutil_copyfile.py.copy',
 '/tmp/example/shutil_copystat.py',
 '/tmp/example/testdata_1.csv',
 '/tmp/example/shutil_study.py',
 '/tmp/example/shutil_copy.py']

copytree()接受两个可调用的参数来控制它的行为。 使用每个目录或子目录的名称以及目录的内容列表来调用ignore参数。 它应该返回被复制的项目的列表。调用copy_function参数用来实际复制文件。

import glob
import pprint
import shutil


def verbose_copy(src, dst):
    print('copying\n {!r}\n to {!r}'.format(src, dst))
    return shutil.copy2(src, dst)


print('BEFORE:')
pprint.pprint(glob.glob('/tmp/example/*'))
print()

shutil.copytree(
    '../shutil', '/tmp/example',
    copy_function=verbose_copy,
    ignore=shutil.ignore_patterns('*.py'),
)

print('\nAFTER:')
pprint.pprint(glob.glob('/tmp/example/*'))

在该示例中,ignore_patterns()用于创建一个忽略函数来跳过复制Python源文件。 verbose_copy()打印复制文件的名称,然后使用copy2()(默认复制功能)来创建副本。输出:

BEFORE:
[]

copying
 '../shutil/example.out'
 to '/tmp/example/example.out'
copying
 '../shutil/file_to_change.txt'
 to '/tmp/example/file_to_change.txt'
copying
 '../shutil/index.rst'
 to '/tmp/example/index.rst'

AFTER:
['/tmp/example/example',
 '/tmp/example/example.out',
 '/tmp/example/file_to_change.txt',
 '/tmp/example/index.rst']

要删除一个目录及其内容,请使用rmtree()。

import glob
import pprint
import shutil

print('BEFORE:')
pprint.pprint(glob.glob('/tmp/example/*'))

shutil.rmtree('/tmp/example')

print('\nAFTER:')
pprint.pprint(glob.glob('/tmp/example/*'))

默认情况下,错误会被作为异常进行处理,但是如果第二个参数是true,则可以忽略它,并且在第三个参数中可以提供一个特殊的错误处理函数。输出:

BEFORE:
['/tmp/example/example',
 '/tmp/example/example.out',
 '/tmp/example/file_to_change.txt',
 '/tmp/example/index.rst']

AFTER:
[]

要将文件或目录从一个位置移动到另一个位置,请使用move()。

import glob
import shutil

with open('example.txt', 'wt') as f:
    f.write('contents')

print('BEFORE: ', glob.glob('example*'))

shutil.move('example.txt', 'example.out')

print('AFTER : ', glob.glob('example*'))

该语义类似于Unix命令mv的语义。如果源和目标在同一个文件系统中,则将源重新命名。否则,源将被复制到目标,然后源被删除。输出:

BEFORE:  ['example.txt']
AFTER :  ['example.out']

Finding Files

shutil.which(cmd, mode=os.F_OK | os.X_OK, path=None):返回可执行文件的路径,如果给定的cmd被调用,它将运行。如果不调用cmd,则返回None。mode是传递给os.access()的权限掩码,默认情况下确定文件是否存在和可执行。当未指定路径时,将使用os.environ()的结果,返回“PATH”值或返回os.defpath

举例
which()函数扫描查找命名文件的搜索路径。 典型的用例是在环境变量PATH中定义的shell的搜索路径上找到一个可执行程序。

import shutil

print(shutil.which('virtualenv'))
print(shutil.which('tox'))
print(shutil.which('no-such-program'))

如果没有找到匹配搜索参数的文件,那么which()返回None。输出:

/Users/dhellmann/Library/Python/3.5/bin/virtualenv
/Users/dhellmann/Library/Python/3.5/bin/tox
None

which()根据文件具有的权限以及要检查的搜索路径接受参数进行过滤。 path参数默认为os.environ('PATH'),但可以是包含以os.pathsep分隔的目录名的任何字符串。 mode参数应该是匹配文件权限的位掩码。 默认情况下,掩码将查找可执行文件,但以下示例使用可读位掩码和备用搜索路径来查找配置文件。

import os
import shutil

path = os.pathsep.join([
    '.',
    os.path.expanduser('~/pymotw'),
])

mode = os.F_OK | os.R_OK

filename = shutil.which(
    'config.ini',
    mode=mode,
    path=path,
)

print(filename)

仍然有竞争条件以这种方式搜索可读文件,因为在找到文件和实际尝试使用文件之间的时间内,文件可以被删除或其权限可以更改。输出:

$ touch config.ini
$ python3 shutil_which_regular_file.py

./config.ini

Archives

shutil.get_archive_formats():返回支持的归档格式列表。返回序列的每个元素都是元组(名称, 描述)。
shutil.make_archive(base_name, format[, root_dir[, base_dir[, verbose[, dry_run[, owner[, group[, logger]]]]]]]):创建归档文件(例如zip或tar)并返回其名称。

  • base_name是要创建的文件的名称,包括路径,减去任何特定于格式的扩展名。format is the archive format: one of “zip”, “tar”, “bztar” (if the bz2 module is available), “xztar” (if the lzma module is available) or “gztar”.
  • root_dir是将成为归档的根目录的目录;例如,在创建归档之前,我们通常将chdir插入到root_dir中。
  • base_dir是我们开始归档的目录;即:base_dir将是归档中所有文件和目录的公共前缀。
  • root_dir和base_dir都默认为当前目录。
  • 如果dry_run为true,则不创建归档,但将执行的操作记录到logger。
  • 所有者和组用于创建tar存档。默认情况下,使用当前所有者和组。
  • logger必须是与 PEP 282兼容的对象,通常为logging.Logger的实例。

shutil.get_unpack_formats():返回分拆的所有注册格式的列表。返回序列的每个元素都是元组(名称, 扩展名, 描述)。
默认情况下,shutil提供以下格式:

  • gztar:gzip的tar文件
  • bztar:bzip2'ed tar文件(如果bz2模块可用)。
  • xztar:xz'ed tar文件(如果lzma模块可用)。
  • tar:未压缩的tar文件
  • zip:ZIP文件

举例
Python的标准库包含许多用于管理诸如tar文件和zipfile等归档文件的模块。在shutil中,还有一些用于创建和提取存档的高级功能。getarchiveformat()返回当前系统支持的格式的名称和描述序列。

import shutil

for format, description in shutil.get_archive_formats():
    print('{:<5}: {}'.format(format, description))

所支持的格式取决于哪些模块和底层库可用,因此这个示例的输出可能基于运行的位置而发生变化。

bztar: bzip2'ed tar-file
gztar: gzip'ed tar-file
tar  : uncompressed tar file
xztar: xz'ed tar-file
zip  : ZIP file

使用makearchive()来创建一个新的归档文件。其输入的目的是为了最好地支持对整个目录及其所有内容进行递归的归档。默认情况下,它使用当前的工作目录,以便所有的文件和子目录都出现在归档的顶层。要更改该行为,请使用rootdir参数移动到文件系统和basedir参数的一个新的相对位置,以指定要添加到归档的目录。

import logging
import shutil
import sys
import tarfile

logging.basicConfig(
    format='%(message)s',
    stream=sys.stdout,
    level=logging.DEBUG,
)
logger = logging.getLogger('pymotw')

print('Creating archive:')
shutil.make_archive(
    'example', 'gztar',
    root_dir='..',
    base_dir='shutil',
    logger=logger,
)

print('\nArchive contents:')
with tarfile.open('example.tar.gz', 'r') as t:
    for n in t.getnames():
        print(n)

这个示例从源目录中开始,用于shutil的示例,并在文件系统中提升一个级别,然后将shutil目录添加到用gzip压缩的tar存档文件中。日志模块被配置为从makearchive()中显示它正在做的事情。

Creating archive:
changing into '..'
Creating tar archive
changing back to '...'

Archive contents:
shutil
shutil/config.ini
shutil/example.out
shutil/file_to_change.txt
shutil/index.rst
shutil/shutil_copy.py
shutil/shutil_copy2.py
shutil/shutil_copyfile.py
shutil/shutil_copyfileobj.py
shutil/shutil_copymode.py
shutil/shutil_copystat.py
shutil/shutil_copytree.py
shutil/shutil_copytree_verbose.py
shutil/shutil_disk_usage.py
shutil/shutil_get_archive_formats.py
shutil/shutil_get_unpack_formats.py
shutil/shutil_make_archive.py
shutil/shutil_move.py
shutil/shutil_rmtree.py
shutil/shutil_unpack_archive.py
shutil/shutil_which.py
shutil/shutil_which_regular_file.py

shutil维护一个可以在当前系统上解压缩的格式的注册表,可以通过getunpackformat()访问。

import shutil

for format, exts, description in shutil.get_unpack_formats():
    print('{:<5}: {}, names ending in {}'.format(
        format, description, exts))

这个注册表不同于创建存档的注册表,因为它还包含用于每个格式的公共文件扩展,以便提取存档的函数可以根据文件扩展来猜测使用哪种格式。

bztar: bzip2'ed tar-file, names ending in ['.tar.bz2', '.tbz2']
gztar: gzip'ed tar-file, names ending in ['.tar.gz', '.tgz']
tar  : uncompressed tar file, names ending in ['.tar']
xztar: xz'ed tar-file, names ending in ['.tar.xz', '.txz']
zip  : ZIP file, names ending in ['.zip']

使用unpackarchive()提取存档,传递存档文件名,并选择应该提取的目录。如果没有给出目录,则使用当前目录。

import pathlib
import shutil
import sys
import tempfile

with tempfile.TemporaryDirectory() as d:
    print('Unpacking archive:')
    shutil.unpack_archive(
        'example.tar.gz',
        extract_dir=d,
    )

    print('\nCreated:')
    prefix_len = len(d) + 1
    for extracted in pathlib.Path(d).rglob('*'):
        print(str(extracted)[prefix_len:])

在这个示例中,unpackarchive()能够确定存档的格式,因为文件名以tar.gz结尾,这个值在解压格式注册表中与gztar格式相关联。

Unpacking archive:

Created:
shutil
shutil/config.ini
shutil/example.out
shutil/file_to_change.txt
shutil/index.rst
shutil/shutil_copy.py
shutil/shutil_copy2.py
shutil/shutil_copyfile.py
shutil/shutil_copyfileobj.py
shutil/shutil_copymode.py
shutil/shutil_copystat.py
shutil/shutil_copytree.py
shutil/shutil_copytree_verbose.py
shutil/shutil_disk_usage.py
shutil/shutil_get_archive_formats.py
shutil/shutil_get_unpack_formats.py
shutil/shutil_make_archive.py
shutil/shutil_move.py
shutil/shutil_rmtree.py
shutil/shutil_unpack_archive.py
shutil/shutil_which.py
shutil/shutil_which_regular_file.py

File System Space

shutil.disk_usage(path):将给定路径的磁盘使用情况统计信息作为named tuple返回总计,使用和免费是总的,已用和可用空间的量,以字节为单位。

举例
在执行一个可能耗尽该空间的长时间运行的操作之前,检查本地文件系统以查看有多少空间是有用的。diskusage()返回一个包含总空间的元组,当前正在使用的数量,以及剩余的空闲量。

import shutil

total_b, used_b, free_b = shutil.disk_usage('.')

gib = 2 ** 30  # GiB == gibibyte
gb = 10 ** 9   # GB == gigabyte

print('Total: {:6.2f} GB  {:6.2f} GiB'.format(
    total_b / gb, total_b / gib))
print('Used : {:6.2f} GB  {:6.2f} GiB'.format(
    used_b / gb, used_b / gib))
print('Free : {:6.2f} GB  {:6.2f} GiB'.format(
    free_b / gb, free_b / gib))

disk_usage()返回的值是字节数,因此示例程序将它们转换为更可读的单元,然后再打印它们。

Total: 499.42 GB  465.12 GiB
Used : 246.68 GB  229.73 GiB
Free : 252.48 GB  235.14 GiB
posted @ 2017-07-11 00:40  ProfiBus  阅读(843)  评论(0编辑  收藏  举报