shutilモジュールを使って、ファイルコピー、ディレクトリ階層のコピーや削除、ファイルやディレクトリの移動(名前変更)を行う方法を説明する。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
前回は、pathlib.Pathクラスを利用したファイル/ディレクトリの操作を見た。今回は、shutilモジュールを使ったより高水準なファイル操作を見てみよう。
shutilモジュールが提供する「高水準なファイル操作」とは、ファイルのコピーやディレクトリツリー全体の削除など、これまでに見てきたシンプルなファイル/ディレクトリの操作よりもさらに複雑な処理のことだ。
shutilモジュールが提供する関数の一部を以下の表にまとめる。
関数 | 説明 |
---|---|
copyfile(src, dst) | srcからdstにファイルをコピーする |
copy(src, dst) | srcからdstにファイルとパーミッションをコピーする。dstがディレクトリなら、その下にsrcの名前でコピーする(内部でcopyfile関数を使用) |
copy2(src, dst) | srcからdstにファイルとパーミッション、その他のメタデータをコピーする。dstがディレクトリなら、その下にsrcの名前でコピーする(内部でcopy関数を使用) |
copytree(src, dst) | srcからdstへディレクトリ階層のコピーを行う |
rmtree(path) | pathが表すディレクトリ階層を削除する |
move(src, dst) | srcからdstにファイルまたはディレクトリを移動(名前変更)する |
shutilが提供するファイル操作関数(一部) |
以下では、これらの関数について見ていこう。
ファイルのコピーには、shutil.copyfile関数、shutil.copy関数、shutil.copy2関数を使える(以下、「shutil.」は省略して表記する)。基本構文を以下に示す(詳細な構文はPythonのドキュメント「shutil.copyfile()」などを参照のこと)。
shutil.copyfile(src, dst)
shutil.copy(src, dst)
shutil.copy2(src, dst)
copyfile関数/copy関数/copy2関数はいずれも、srcが指すコピー元のファイルの内容を、dstが指すコピー先のファイルにコピーする。それらの違いを以下にまとめる。
これらの関数は、コピー先ファイルのパスを表す文字列またはPathオブジェクトを戻り値とする。
copyfile関数はファイルの内容をコピーするだけであるのに対して、copy関数やcopy2関数はそのファイルが持つメタデータを(一部ではあっても)コピー先のファイルに維持しようとする。ただし、その全てを維持できるわけではないことに注意しよう(Pythonのドキュメント「shutil --- 高水準のファイル操作」の冒頭にも「警告 高水準のファイルコピー関数 (shutil.copy(), shutil.copy2()) でも、ファイルのメタデータの全てをコピーすることはできません」とある)。
パラメーター | 説明 |
---|---|
src | コピー元のファイル |
dst | コピー先のファイル |
copyfile関数/copy関数/copy2関数のパラメーター |
実際に使用例を示す前に、準備をしておこう。
import os
from pathlib import Path
import shutil
Path('xxx.txt').write_text('xxx')
os.chmod('xxx.txt', 0o777)
このコードでは「xxx.txt」というテキストファイルを作成して、そのパーミッションをos.chmodメソッドを使って変更している(Windows環境ではos.chmod関数は「読み出し専用フラグの設定」しかできないので、この例はUNIX上のJupyter Notebook環境で試すのがよいだろう)。ファイルが実際にどのようなパーミッションを持っているかは「ls -l」コマンドで確かめられる(Windows環境では「ls」コマンドを実行できる。このときには、dirコマンドに付加するオプションを渡せる)。
ls -l
これを実行すると次のように表示される。
xxx.txtのタイムスタンプとパーミッション(rwxrwxrwx)に注目しよう(これは全てのユーザーに対して、そのファイルを読み取り/書き込み/実行を許すことを示している)。では、実際のファイルコピーを行ってみよう。
shutil.copyfile('xxx.txt', 'aaa.txt')
shutil.copy(Path('xxx.txt'), Path('bbb.txt'))
shutil.copy2('xxx.txt', 'ccc.txt')
上のコードは、今紹介した3つの関数を使ってファイルをコピーしているだけだ。ここで先ほどと同様に「ls -l」コマンドでファイルの詳細情報を表示してみよう。以下に実行結果を示す。
copyfile関数を使ってコピーしたaaa.txtファイルはタイムスタンプもパーミッションも元のファイルとは異なっている。copy関数でコピーしたbbb.txtファイルを見ると、パーミッションは同じだが、タイムスタンプは異なっている。copy2関数でコピーしたccc.txtファイルはパーミッションとタイムスタンプが同じになっている(ただし、全てのメタデータをコピーできるわけではないことは既に述べた通りだ)。
copy関数の第2引数にディレクトリを指定した場合も見ておこう(copyfile関数は第2引数にディレクトリを指定すると例外を発生することに注意。copy関数とcopy2関数の第2引数にディレクトリを指定できるのは、copy関数内部で第2引数がディレクトリかどうかを調べて、その場合には「ディレクトリ名+第1引数に指定したファイル名」をコピー先のファイル名としてcopyfile関数を呼び出すようにしているからだ)。
Path('foo').mkdir()
shutil.copy('xxx.txt', 'foo')
「ls -l foo」コマンドでfooディレクトリの内容を表示すると、次のようになる。
fooディレクトリにファイルがコピーされたのが分かるはずだ。
GitHubで公開されているshutilモジュールのソースコードを確認すると分かるが、copy2関数は内部でcopy関数を使用しており(その後、本稿では扱っていないshutilモジュールのcopystat関数を呼び出して、ファイルのメタデータをコピーしている)、copy関数は内部でcopyfile関数を使用している(その後、同じく本稿で扱っていないshutilモジュールのcopymode関数でファイルのパーミッションをコピーしている)。Python 3.8以降では、copyfile関数は内部でOS依存の高速にファイルコピーを実行するシステムコールを呼び出すようになっている。
このようにcopy関数とcopy2関数によるファイルのコピーでは、最終的にcopyfile関数が呼び出され、その後、必要に応じてパーミッションやメタデータのコピーが行われるようになっている。3つの関数があるのは、こうした理由からだ(copytree関数とmove関数も内部ではデフォルトでcopy2関数を呼び出すような実装になっているので、最終的にはそこからcopyfile関数が呼び出される)。
Copyright© Digital Advantage Corp. All Rights Reserved.