Python3标准库:shutil高层文件操作

1. shutil高层文件操作

shutil模块包括一些高层文件操作,如复制和归档。

1.1 复制文件

copyfile()将源文件的内容复制到目标文件,如果没有权限写目标文件,则会产生IOError。

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.*'))

由于这个函数会打开输入文件进行读取,而不论其类型,所以某些特殊文件(如UNIX设备节点)不能用copyfile()复制为新的特殊文件。

copyfile()的实现使用了底层函数copyfileobj()。copyfile()的参数是文件名,但copyfileobj()的参数是打开的文件句柄。还可以有第三个参数(可选):读入块使用的一个缓冲区长度。 

import io
import os
import shutil
import sys


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会一次读入所有输入,或者也可以使用其他正数,这会设置特定的块大小。下面这个例子将使用多个不同的块大小来展示效果。

类似于UNIX命令行工具cp,copy()函数会用同样的方式解释输出名。如果指定的目标指示一个目录而不是一个文件,则会使用源文件的基名在该目录中创建一个新文件。 

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/*'))

一同复制文件的权限与内容。

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')

这个新文件的所有特性读与原文件完全相同。

1.2 复制文件元数据

默认的,在UNIX下创建一个新的文件时,它会根据当前用户的umask接受权限。要把权限从一个文件复制到另一个文件,可以使用copymode()。

import os
import shutil

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()将脚本的权限复制到示例文件。

要为文件复制到其他元数据,可以使用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()只会复制与文件关联的权限和日期。

1.3 处理目录树

shutil包含3个函数来处理目录树。要把一个目录从一个位置复制到另一个位置,可以使用copytree()。这个函数会递归遍历源目录树,将文件复制到目录位置。目标目录必须不存在。 

import glob
import pprint
import shutil

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

shutil.copytree('../shutil', 'C')

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

symlinks参数控制符号链接作为链接复制或文件复制。默认的会将内容复制到新文件。如果这个选项为true,则会在目标树中创建新的符号链接。

copytree()接受两个callabe参数来控制它的行为。调用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(
    'E:/pythondaima/1l', '/tmp/example',
    copy_function=verbose_copy,
    ignore=shutil.ignore_patterns('*.py'),
)

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

在这个例子中,使用了ignore_patterns()来创建一个ignore函数,要求不复制Python源文件。verbose_copy()首先打印复制文件时的文件名;然后使用copy2()(即默认的复制函数)来建立副本。

要删除一个目录以及其中的内容,可以使用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,则可以忽略这些错误。可以在第三参数中提供一个特殊的错误处理函数。

要把一个文件或目录从一个位置移动到另一个位置,可以使用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类似。如果源和目标都在同一个文件系统中,则会重命名源文件。否则,源文件会被复制到目标文件,然后被删除。

1.4 查找文件

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

import shutil

print(shutil.which('python'))
print(shutil.which('time'))
print(shutil.which('java'))

如果无法找到与搜索参数匹配的文件,则which()返回None。

1.5 归档

Python的标准库包含很多模块来管理归档文件,如tarfile和zipfile。另外,shutil中还提供了很多更高层函数来创建和解压归档文件。get_archive_formats()返回当前系统上支持的所有格式的名字和描述。

import shutil

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

支持的格式取决于有哪些模块和底层库。因此,根据这个例子在哪里运行,它的输出可能会有变化。

shutil维护了一个格式注册表,可以在当前系统上解包;可以通过get_unpack_formats()访问这个注册表。

import shutil

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

shutil管理的注册表不同于创建归档所用的注册表,因为它还包括对应各种格式的常见文件扩展名。解压缩归档文件的函数使用这个注册表来根据文件扩展名猜测应当使用哪种格式。

1.6 文件系统空间

完成一个长时间运行的可能耗尽可用的空间的操作之前,最好先检查本地文件系统,来看看有多少可用的空间,这会很有用。disk_usage()会返回一个元组,包括总空间、当前正在使用的空间以及未用的空间(自由空间)。

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()返回的值以字节为单位,所以这个示例程序在打印之前先把它转换为更可读的一些单位。

posted @ 2020-03-17 08:52  SmallGrayCode  阅读(244)  评论(0编辑  收藏  举报