[解決!Python]ファイルやディレクトリをコピーするには解決!Python

shutilモジュールが提供するcopy/copy2/copytree関数を使って、ファイルやディレクトリをコピーする方法を紹介する

» 2022年08月02日 05時00分 公開
[かわさきしんじDeep Insider編集部]

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

「解決!Python」のインデックス

連載目次

import shutil

with open('a.txt', 'w'):  # サンプルファイルの作成
    pass

ret = shutil.copy('a.txt', 'b.txt')
print(ret)  # b.txt:コピー後のファイル名が戻り値

shutil.copy('b.txt', 'a.txt'# OK:既存ファイルは上書きされる

# コピーされるものの比較
import os

os.chmod('a.txt', 0o777# a.txtファイルのパーミションを変更

shutil.copy('a.txt', 'b.txt'# パーミッションだけをコピーする
shutil.copy2('a.txt', 'c.txt'# パーミッション以外の属性もコピーする
shutil.copyfile('a.txt', 'd.txt'# 属性をコピーしない

# ファイルをディレクトリの下にコピー
os.mkdir('foo'# サンプルディレクトリの作成

ret = shutil.copy('a.txt', 'foo'# dstにディレクトリを指定する
print(ret)  # foo/a.txt:指定したディレクトリの下にファイルをコピー

shutil.copy2('b.txt', 'foo/bar'# b.txtがfoo/barファイルとしてコピーされる

# ディレクトリのコピー
ret = shutil.copytree('foo', 'bar')
print(ret)  # bar:コピー先のディレクトリ名が戻り値

shutil.copytree('a.txt', 'baz'# NotADirectoryError
shutil.copytree('foo', 'a.txt'# FileExistsError

# 既存のディレクトリに再帰的にコピーする
shutil.copytree('foo', 'bar'# FileExistsError
shutil.copytree('foo', 'bar', dirs_exist_ok=True# OK

# コピーに使用する関数を指定する
shutil.copytree('foo', 'baz', copy_function=shutil.copy)

# コピーしないものを指定
from shutil import ignore_patterns

shutil.copytree('.', 'qux', ignore=ignore_patterns('foo', 'bar', 'baz', '*.py'))


関数 コピーするもの 動作
copy(src, dst) ファイル srcに指定されるファイルをdstにコピーする。dstにはファイルやディレクトリを指定可能。ファイルのパーミッションもコピーする
copy2(src, dst) ファイル srcで指定されるファイルをdstにコピーする。dstにはディレクトリを指定可能。ファイルのパーミッション、最終アクセス時間、最終変更時間などもコピーする
copytree(src, dst) ディレクトリの内容 srcに指定されたディレクトリの内容を、dstに指定されたディレクトリに再帰的にコピーする。srcとdstにはディレクトリを指定する。コピーに使用する関数を指定するcopy_functionパラメーターや、コピー先のディレクトリが存在しているときにコピーするかを指定するdirs_exist_okパラメーター、コピーの対象外とするファイル/ディレクトリを指定するignoreパラメーターなども指定可能
copy/copy2/copytree関数の動作

ファイルのコピー

 特定のファイルをコピーするにはshutilモジュールのcopy関数かcopy2関数を使用する。これら2つの関数の違いは、コピー時にファイルのパーミッションをコピーするか(copy関数)、パーミッションに加えて最終アクセス時間、最終更新時間などの属性もコピーするか(copy2関数)である。

shutil.copy(src, dest)
shutil.copy2(src, dest)


 これらの関数はfollow_symlinksパラメーターも指定可能だが、本稿では説明は省略する。

 以下にcopy関数の使用例を示す。

import shutil

with open('a.txt', 'w'):  # サンプルファイルの作成
    pass

ret = shutil.copy('a.txt', 'b.txt')
print(ret)  # b.txt:コピー後のファイル名が戻り値


 この例では、「a.txt」というファイルを「b.txt」という名前でコピーしている。なお、copy/copy2関数の戻り値はコピーされたファイルの名前となる。なお、本稿で紹介する関数は全てsrcとdstに文字列かpathlibモジュールのPathクラスのインスタンスを指定できる。

 既存のファイルをdstに指定した場合、例外は発生せずに上書きされる。

shutil.copy('b.txt', 'a.txt'# OK:既存ファイルは上書きされる


 既に述べたが、copy関数とcopy2関数の違いはファイルのパーミッションだけをコピーするか、パーミッションに加えて最終アクセス時間、最終更新時間などの属性もコピーするかにある。そこで、上で作成したa.txtファイルの属性を変更、b.txtファイルを削除した上で以下のコードを実行し、その結果を調べてみよう。

import os

os.chmod('a.txt', 0o777# a.txtファイルのパーミションを変更

shutil.copy('a.txt', 'b.txt'# パーミッションだけをコピーする
shutil.copy2('a.txt', 'c.txt'# パーミッション以外の属性もコピーする
shutil.copyfile('a.txt', 'd.txt'# 属性をコピーしない


 この例では、copy関数とcopy2関数に加えて、shutilモジュールで定義されているcopyfile関数も使用した。copyfile関数はsrcに指定されたファイルを、destに指定されたファイルにコピーする。ただし、属性のコピーは何も行わない。copy関数とcopy2関数は内部でこの関数を使用した上で、やはりshutilモジュールで定義されているcopymode関数やcopystat関数を使って、パーミッションやその他の属性をコピーしている。

 上記のコードをmacOS上で動作するVisual Studio CodeのPython対話環境で実行した後で、等号ターミナルでa.txt/b.txt/c.txt/d.txtの各ファイルの情報を表示したところを以下に示す。

コピー関数の動作の違い コピー関数の動作の違い

 a.txtファイルがオリジナルだ。copy関数でコピーしたb.txtファイルについてはパーミッションが同じになっているが、タイムスタンプが異なっている。一方、copy2関数でコピーしたc.txtファイルについてはパーミッションもタイムスタンプと同じだ。最後に、copyfile関数でコピーしたd.txtファイルについてはパーミッションもタイムスタンプも異なっている(なお、本稿ではcopyfile関数については詳しい説明は行わない)。

 なお、これらの関数を使っても、ファイルが持つ全てのメタデータをコピーできるわけではない点には注意されたい。詳しくはshutilモジュールのドキュメントを参照されたい。

 また、copy関数とcopy2関数でdstにディレクトリを指定すると、srcに指定したファイルがdstに指定したディレクトリ以下にコピーされる。

 以下に例を示す。

os.mkdir('foo'# サンプルディレクトリの作成

ret = shutil.copy('a.txt', 'foo'# dstにディレクトリを指定する
print(ret)  # foo/a.txt:指定したディレクトリの下にファイルをコピー


 このとき、dstに存在しないディレクトリを指定すると、その名前でファイルがコピーされるので注意しよう(存在しないディレクトリを再帰的に作るようなことはない)。

shutil.copy2('b.txt', 'foo/bar'# b.txtがfoo/barファイルとしてコピーされる


 例えば、上のコードは恐らくb.txtファイルを「foo/bar」ディレクトリにコピーするつもりだったのだろうが、実際にはb.txtファイルがfooディレクトリの下にbarというファイル名でコピーされる。

 また、深いディレクトリ階層を指定したときに、その中に存在しないディレクトリがあれば例外が発生する。

ディレクトリの再帰的なコピー

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

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

メールマガジン登録

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