連載
» 2019年11月01日 05時00分 公開

Python入門:[Python入門]shutilモジュールによる高水準ファイル操作 (1/3)

shutilモジュールを使って、ファイルコピー、ディレクトリ階層のコピーや削除、ファイルやディレクトリの移動(名前変更)を行う方法を説明する。

[かわさきしんじ,Deep Insider編集部]

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

「Python入門」のインデックス

連載目次

 前回は、pathlib.Pathクラスを利用したファイル/ディレクトリの操作を見た。今回は、shutilモジュールを使ったより高水準なファイル操作を見てみよう。

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()」などを参照のこと)。

copyfile関数/copy関数/copy2関数

shutil.copyfile(src, dst)
shutil.copy(src, dst)
shutil.copy2(src, dst)


 copyfile関数/copy関数/copy2関数はいずれも、srcが指すコピー元のファイルの内容を、dstが指すコピー先のファイルにコピーする。それらの違いを以下にまとめる。

  • copyfile関数:コピー元のデータの内容だけをコピーする。また、srcとdstには文字列またはパスライクオブジェクト(前回紹介したpathlib.Pathクラスのインスタンスなど)を指定できる
  • copy関数:コピー元のデータの内容とパーミッション(ファイルへのアクセス権情報)をコピーする。srcとdstには文字列だけを受け付ける(Pythonのドキュメント「shutil.copy()」では「src と dst は両方共文字列でなければなりません」とあるが、筆者が試したところではPathクラスのインスタンスも受け付けるようだ)。dstがディレクトリを表している場合、srcはそのディレクトリの下にコピーされる
  • copy2関数:ファイルのメタデータ(作成時間、変更時間、その他の情報)も可能な限りコピーしようとすることを除けばcopy関数と同様

 これらの関数は、コピー先ファイルのパスを表す文字列または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')

copyfile関数/copy関数/copy2関数の使用例

 上のコードは、今紹介した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')

copyfile関数でファイルをディレクトリにコピーする

 「ls -l foo」コマンドでfooディレクトリの内容を表示すると、次のようになる。

実行結果 実行結果

 fooディレクトリにファイルがコピーされたのが分かるはずだ。

コラム:copyfile関数、copy関数、copy2関数の関係

 GitHubで公開されているshutilモジュールのソースコードを確認すると分かるが、copy2関数は内部でcopy関数を使用しており(その後、本稿では扱っていないshutilモジュールのcopystat関数を呼び出して、ファイルのメタデータをコピーしている)、copy関数は内部でcopyfile関数を使用している(その後、同じく本稿で扱っていないshutilモジュールのcopymode関数でファイルのパーミッションをコピーしている)。Python 3.8以降では、copyfile関数は内部でOS依存の高速にファイルコピーを実行するシステムコールを呼び出すようになっている。

 このようにcopy関数とcopy2関数によるファイルのコピーでは、最終的にcopyfile関数が呼び出され、その後、必要に応じてパーミッションやメタデータのコピーが行われるようになっている。3つの関数があるのは、こうした理由からだ(copytree関数とmove関数も内部ではデフォルトでcopy2関数を呼び出すような実装になっているので、最終的にはそこからcopyfile関数が呼び出される)。


       1|2|3 次のページへ

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。