Table Of Contents

Previous topic

PyMOTW: optparse

Next topic

PyMOTW: urlparse

This Page

PyMOTW: shutil

  • 模块:shutil
  • 目的: 高层次的文件操作.
  • python版本:1.4+

shutil模块提供了一些高层次的文件操作, 比如复制, 设置权限等等.

描述:

shutil模块提供了一些用于复制和删除整个文件的函数.

复制文件:

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

import os
from shutil import *

print 'BEFORE:', os.listdir(os.getcwd())
copyfile('shutil_copyfile.py', 'shutil_copyfile.py.copy')
print 'AFTER:', os.listdir(os.getcwd())
$ python shutil_copyfile.py
BEFORE: ['__init__.py', 'shutil_copyfile.py']
AFTER: ['__init__.py', 'shutil_copyfile.py', 'shutil_copyfile.py.copy']

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

import os
from StringIO import StringIO
import sys
from shutil import *

class VerboseStringIO(StringIO):
    def read(self, n=-1):
       next = StringIO.read(self, n)
       print 'read(%d) =>' % n, 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 = StringIO()
copyfileobj(input, output)

print

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

print

print 'Blocks of 20:'
input = VerboseStringIO(lorem_ipsum)
output = StringIO()
copyfileobj(input, output, 20)

默认的行为是以大块读取:

$ python shutil_copyfileobj.py
Default:
read(16384) => 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.
read(16384) =>

使用-1表示一次性读取所有输入:

All at once:
read(-1) => 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.
read(-1) =>

使用一个正整数设置块大小:

Blocks of 20:
read(20) => Lorem ipsum dolor si
read(20) => t amet, consectetuer
read(20) =>  adipiscing elit.
V
read(20) => estibulum aliquam mo
read(20) => llis dolor. Donec vu
read(20) => lputate nunc ut diam
read(20) => .
Ut rutrum mi vel
read(20) => sem. Vestibulum ante
read(20) =>  ipsum.
read(20) =>

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

import os
from shutil import *

os.mkdir('example')
print 'BEFORE:', os.listdir('example')
copy('shutil_copy.py', 'example')
print 'AFTER:', os.listdir('example')
$ python shutil_copy.py
BEFORE: []
AFTER: ['shutil_copy.py']

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

import os
from shutil import *

def show_file_info(filename):
    stat_info = os.stat(filename)
    print '\tMode    :', stat_info.st_mode
    print '\tCreated :', time.ctime(stat_info.st_ctime)
    print '\tAccessed:', time.ctime(stat_info.st_atime)
    print '\tModified:', time.ctime(stat_info.st_mtime)

os.mkdir('example')
print 'SOURCE:'
show_file_info('shutil_copy2.py')
copy2('shutil_copy2.py', 'example')
print 'DEST:'
show_file_info('example/shutil_copy2.py')
$ python shutil_copy2.py

SOURCE:
        Mode    : 33188
        Created : Sun Oct 21 15:16:07 2007
        Accessed: Sun Oct 21 15:16:11 2007
        Modified: Sun Oct 21 15:16:07 2007
        DEST:
        Mode    : 33188
        Created : Sun Oct 21 15:16:11 2007
        Accessed: Sun Oct 21 15:16:11 2007
        Modified: Sun Oct 21 15:16:07 2007

复制文件元信息:

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

from commands import *
from shutil import *

print 'BEFORE:', getstatus('file_to_change.txt')
copymode('shutil_copymode.py', 'file_to_change.txt')
print 'AFTER :', getstatus('file_to_change.txt')

首先, 需要创建一个文件. 然后对权限做些修改:

$ touch file_to_change.txt
$ chmod ugo+w file_to_change.txt

然后, 运行刚才的示例脚本会改变之前的权限:

$ python shutil_copymode.py
BEFORE: -rw-rw-rw-   1 dhellman  dhellman  0 Oct 21 14:43 file_to_change.txt
AFTER : -rw-r--r--   1 dhellman  dhellman  0 Oct 21 14:43 file_to_change.txt

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

import os
from shutil import *
import time

def show_file_info(filename):
    stat_info = os.stat(filename)
    print '\tMode    :', stat_info.st_mode
    print '\tCreated :', time.ctime(stat_info.st_ctime)
    print '\tAccessed:', time.ctime(stat_info.st_atime)
    print '\tModified:', time.ctime(stat_info.st_mtime)

print 'BEFORE:'
show_file_info('file_to_change.txt')
copystat('shutil_copystat.py', 'file_to_change.txt')
print 'AFTER :'
show_file_info('file_to_change.txt')
$ python shutil_copystat.py
BEFORE:
        Mode    : 33206
        Created : Sun Oct 21 15:01:23 2007
        Accessed: Sun Oct 21 14:43:26 2007
        Modified: Sun Oct 21 14:43:26 2007
        AFTER :
        Mode    : 33188
        Created : Sun Oct 21 15:01:44 2007
        Accessed: Sun Oct 21 15:01:43 2007
        Modified: Sun Oct 21 15:01:39 2007

目录树:

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

Note

注意:copytree()文档中说, 它一般作为一个样本实现, 而不是一个工具. 你可以修改其源码, 让它变得更稳定, 或者增加一些功能, 比如说进度条.

from commands import *
from shutil import *

print 'BEFORE:'
print getoutput('ls -rlast /tmp/example')
copytree('example', '/tmp/example')
print 'AFTER:'
print getoutput('ls -rlast /tmp/example')
$ python shutil_copytree.py
BEFORE:
ls: /tmp/example: No such file or directory
AFTER:
total 8
8 -rw-r--r--    1 dhellman  wheel  1627 Oct 21 15:16 shutil_copy2.py
0 drwxr-xr-x    3 dhellman  wheel   102 Oct 21 15:16 .
0 drwxrwxrwt   18 root      wheel   612 Oct 21 15:26 ..

使用rmtree()可以删除整个目录树. 里面若产生错误会作为异常抛出. 但是如果它的第二个参数是目录树, 那么错误会被忽略, 第三个参数可以指定为一个特殊出错处理函数句柄.

from commands import *
from shutil import *

print 'BEFORE:'
print getoutput('ls -rlast /tmp/example')
rmtree('example', '/tmp/example')
print 'AFTER:'
print getoutput('ls -rlast /tmp/example')
$ python shutil_rmtree.py

BEFORE:
total 8
8 -rw-r--r--    1 dhellman  wheel  1627 Oct 21 15:16 shutil_copy2.py
0 drwxr-xr-x    3 dhellman  wheel   102 Oct 21 15:16 .
0 drwxrwxrwt   18 root      wheel   612 Oct 21 15:26 ..
AFTER:
ls: /tmp/example: No such file or directory

移动文件或目录可以使用move(), 这很类似于Unix命令mv. 如果源文件或目录和目标文件或目录在同一个文件系统下, 那么源文件或目录会直接重命名. 否则源文件或目录会复制到目标文件或目录, 接着删除源文件或目录.

import os
from shutil import *

print 'BEFORE: example : ', os.listdir('example')
move('example', 'example2')
print 'AFTER : example2: ', os.listdir('example2')
$ python shutil_move.py
BEFORE: example :  ['shutil_copy.py']
AFTER : example2:  ['shutil_copy.py']

参考