shutilモジュールが提供するcopy/copy2/copytree関数を使って、ファイルやディレクトリをコピーする方法を紹介する
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
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.