shutil – 高レベルなファイル操作

目的:高レベルなファイル操作
利用できるバージョン:1.4 以上

shutil モジュールは、コピーやパーミッション設定といった高レベルなファイル操作を提供します。

ファイルをコピーする

copyfile() はコピー元ファイルの内容をコピー先ファイルへコピーします。もしコピー先ファイルへの書き込みパーミッションがない場合、 IOError が発生します。この理由は、ファイル種別に関わらず、そのコピー元ファイルをオープンして読み込もうとしますが、 copyfile() では特殊ファイルが新たなファイルとしてコピーされないからです。

from shutil import *
from glob import glob

print 'BEFORE:', glob('shutil_copyfile.*')
copyfile('shutil_copyfile.py', 'shutil_copyfile.py.copy')
print 'AFTER:', glob('shutil_copyfile.*')
$ python shutil_copyfile.py
BEFORE: ['shutil_copyfile.py']
AFTER: ['shutil_copyfile.py', 'shutil_copyfile.py.copy']

copyfile() は、低レベル関数に copyfileobj() を使用して書き込みます。 copyfile() への引数はファイル名であるのに対して、 copyfileobj() への引数はオープンされたファイルハンドラです。オプションである3番目の引数はチャンクの読み込みに使用するバッファ長です(デフォルトでは、一度にファイル全てを読み込む)。

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

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)

デフォルトの動作は大きなブロックで読み込みます。その入力の全てを一度に読み込むには -1 を、独自のブロックサイズで読み込むには自然数を指定してください。

$ 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) =>

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 のように出力ファイルの名前を解釈します。コピー先ファイルの名前がファイルではなくディレクトリなら、ソースファイルのベース名を使用してそのディレクトリ配下に新たなファイルが作成されます。そのファイルのパーミッションはコピー元ファイルと同じになります。

from shutil import *
import os

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() のように動作しますが、コピー元ファイルのアクセス時刻と更新時刻のメタデータもコピー先ファイルへ保持されます。

from shutil import *
import os
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)

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 : Sat Jul 16 12:19:08 2011
        Accessed: Sun Feb 17 11:33:33 2013
        Modified: Fri Feb 11 08:38:29 2011
DEST:
        Mode    : 33188
        Created : Sun Feb 17 11:33:33 2013
        Accessed: Sun Feb 17 11:33:33 2013
        Modified: Fri Feb 11 08:38:29 2011

ファイルのメタデータをコピーする

デフォルトでは、Unix 環境で新たなファイルを作成するとき、そのファイルはカレントユーザの umask に基づいてパーミッションが決まります。あるファイルから別のファイルへそのパーミッションをコピーするには copymode() を使用してください。

from shutil import *
from commands import *
import os

f = open('file_to_change.txt', 'wt')
f.write('content')
f.close()
os.chmod('file_to_change.txt', 0444)

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

まず変更対象のファイルを作成します。

#!/bin/sh
# Set up file needed by shutil_copymode.py
touch file_to_change.txt
chmod ugo+w file_to_change.txt

パーミッションを変更するサンプルスクリプトを実行します。

$ python shutil_copymode.py
BEFORE: -r--r--r--  1 dhellmann  dhellmann  7 Feb 17 11:33 file_to_change.txt
AFTER : -rw-r--r--  1 dhellmann  dhellmann  7 Feb 17 11:33 file_to_change.txt

その他のファイルに関するメタデータ(パーミッション、最終アクセス時刻、最終更新時刻)をコピーするには copystat() を使用してください。

from shutil import *
import os
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)

f = open('file_to_change.txt', 'wt')
f.write('content')
f.close()
os.chmod('file_to_change.txt', 0444)

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    : 33060
        Created : Sun Feb 17 11:33:33 2013
        Accessed: Sun Feb 17 11:33:33 2013
        Modified: Sun Feb 17 11:33:33 2013
AFTER :
        Mode    : 33188
        Created : Sun Feb 17 11:33:33 2013
        Accessed: Sun Feb 17 11:33:33 2013
        Modified: Fri Feb 11 08:38:29 2011

ディレクトリツリーを扱う

shutil モジュールは、ディレクトリツリーを扱う3つの関数を提供します。ある場所から別の場所へディレクトリをコピーするには、 copytree() を使用してください。それはコピー元のディレクトリツリーを再帰的に辿り、コピー先へファイルをコピーします。コピー先のディレクトリは前もって存在していなければなりません。 symlinks 引数は、シンボリックリンクをリンクとしてコピーするか、ファイルとしてコピーするかを制御します。デフォルトは新しいファイルとしてコピーします。そのオプションが True の場合、コピー先ツリーに新たなシンボリックリンクが作成されます。

Note

copytree() のドキュメントには、この関数はツールというよりサンプル実装と見なすようにと説明されています。その実装をコピーして、もっと堅牢にするか、進捗が分かるような機能を追加してください。

from shutil import *
from commands 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 dhellmann  wheel  1595 Feb 11  2011 shutil_copy2.py
0 drwxrwxrwt  18 root       wheel   612 Feb 17 11:33 ..
0 drwxr-xr-x   3 dhellmann  wheel   102 Feb 17 11:33 .

ディレクトリとその中にあるファイルを削除するには rmtree() を使用してください。デフォルトでは例外としてエラーが発生しますが、2番目の引数を True にすることでそのエラーは無視されます。そして、特殊なエラーハンドラ関数が3番目の引数で提供されます。

from shutil import *
from commands import *

print 'BEFORE:'
print getoutput('ls -rlast /tmp/example')
rmtree('/tmp/example')
print 'AFTER:'
print getoutput('ls -rlast /tmp/example')
$ python shutil_rmtree.py
BEFORE:
total 8
8 -rw-r--r--   1 dhellmann  wheel  1595 Feb 11  2011 shutil_copy2.py
0 drwxrwxrwt  18 root       wheel   612 Feb 17 11:33 ..
0 drwxr-xr-x   3 dhellmann  wheel   102 Feb 17 11:33 .
AFTER:
ls: /tmp/example: No such file or directory

ある場所から別の場所へファイルやディレクトリを移動するには move() を使用してください。Unix コマンドの mv とほとんど同じ仕組みで動作します。同じファイルシステム上にコピー元とコピー先のファイルがある場合、そのコピー元が単純にリネームされます。それ以外の場合は、コピー先へコピーされた後でコピー元が削除されます。

from shutil import *
from glob import glob

f = open('example.txt', 'wt')
f.write('contents')
f.close()

print 'BEFORE: ', glob('example*')
move('example.txt', 'example.out')
print 'AFTER : ', glob('example*')
$ python shutil_move.py
BEFORE:  ['example.txt']
AFTER :  ['example.out']

See also

shutil
本モジュールの標準ライブラリドキュメント
ファイルアクセス
ファイルを扱うその他のモジュール
Bookmark and Share