【Python】ディレクトリをコピーする方法!shutil.copytree()の使い方!

この記事ではPythonのディレクトリ(フォルダ)を丸ごとコピーする関数『shutil.copytree()』について、

  • shutil.copytree()とは
  • shutil.copytree()の構文
  • shutil.copytree()の使い方
    • ディレクトリを新規のディレクトリにコピーする方法
    • ディレクトリを既存のディレクトリにコピーする方法
  • shutil.copytree()のオプション
    • symlinks引数
    • ignore引数
    • copy_function引数
    • ignore_dangling_symlinks引数
    • dirs_exist_ok引数

などをサンプルコードを用いて分かりやすく説明するように心掛けています。ご参考になれば幸いです。

shutil.copytree()とは

shutil.copytree()は、Pythonの標準ライブラリshutilに含まれる関数で、ディレクトリ(フォルダ)とその内容を再帰的にコピーするために使用されます。コピー元ディレクトリ内のすべてのファイルとサブディレクトリがコピーされます。

後ほどshutil.copytree()の構文や使い方について詳しく説明しますが、まず以下に示す簡単なサンプルコードを見てみましょう。

import shutil

# コピー元ディレクトリパス
src_dir = 'testdir/dir1'
# コピー先ディレクトリパス
dst_dir = 'testdir/dir2'

# shutil.copytree()でtestdir/dir1ディレクトリの中身を丸ごとtestdir/dir2ディレクトリにコピー
shutil.copytree(src_dir, dst_dir)

上記のサンプルコードでは、testdir/dir1ディレクトリをtestdir/dir2ディレクトリにコピーしています。shutil.copytree(src_dir, dst_dir)は、コピー元のディレクトリsrc_dirの内容をコピー先のディレクトリdst_dirに再帰的にコピーします。この操作により、testdir/dir1ディレクトリ内のすべてのファイルとサブディレクトリがtestdir/dir2ディレクトリに複製されます。

公式ドキュメント

shutil.copytree()の公式ドキュメントのリンクを以下に示します。

この記事ではshutil.copytree()の「使い方」や「オプション」についてできる限り分かりやすく説明しています。これらの関数の詳細を知りたい方は、公式ドキュメントをご覧ください。

shutil.copytree()の構文

shutil.copytree()の構文を以下に示します。

構文

shutil.copytree(src, dst, symlinks=False, ignore=None, copy_function=shutil.copy2, ignore_dangling_symlinks=False, dirs_exist_ok=False)

shutil.copytree()の引数と返り値を以下に示します。

引数

  • src
    • コピー元のディレクトリパスです。
  • dst
    • コピー先のディレクトリパスです。新規に作成されるディレクトリパスを指定します。
    • コピー先ディレクトリが既に存在する場合にはエラーになりますが、引数dirs_exist_okTrueに設定すると、エラーが発生しなくなります。
  • symlinks(省略可能)
    • シンボリックリンクをコピーするかどうかを指定します。デフォルトはFalseです。
  • ignore(省略可能)
    • コピー対象外とするファイルを指定するための関数を指定します。
  • copy_function(省略可能)
    • ファイルをコピーするために使用する関数を指定します。デフォルトはshutil.copy2です。
  • ignore_dangling_symlinks(省略可能)
    • ぶら下がりシンボリックリンクを無視するかどうかを指定します。デフォルトはFalseです。
  • dirs_exist_ok(省略可能)
    • コピー先ディレクトリが存在する場合にエラーを発生させないかどうかを指定します。デフォルトは False です。

返り値(戻り値)

  • コピー先ディレクトリのパスを返します。

shutil.copytree()の使い方

以下のディレクトリ構造とします。

testdir/
├── dir1/
│   ├── subdir/
│   │   └── file1.txt
│   └── file2.txt
└── dir2/

このディレクトリ構成で、shutil.copytree()について、以下に示している使い方をこれから説明します。

  • ディレクトリを新規のディレクトリにコピーする方法
  • ディレクトリを既存のディレクトリにコピーする方法

上記の使い方について順番に説明します。

ディレクトリを新規のディレクトリにコピーする方法

shutil.copytree()の第2引数dstに新規のディレクトリパス(存在しないディレクトリのパス)を指定すると、コピー元のディレクトリsrcの中身を丸ごと、新規のディレクトリパスdstの中にコピーします。

サンプルコード

import shutil

src_dir = 'testdir/dir1'
dst_dir = 'testdir/dir3'

shutil.copytree(src_dir, dst_dir)

上記のサンプルコード実行後のディレクトリ構成

testdir/
├── dir1/
│   ├── subdir/
│   │   └── file1.txt
│   └── file2.txt
├── dir2/
└── dir3/
    ├── subdir/
    │   └── file1.txt
    └── file2.txt

ディレクトリを既存のディレクトリにコピーする方法

shutil.copytree()の第2引数dstに既存のディレクトリパス(存在するディレクトリのパス)を指定すると、デフォルトではエラーになります。この場合には、引数dirs_exist_okTrueにするとエラーになりならず、コピー元のディレクトリsrcの中身を丸ごと、既存のディレクトリパスdstの中にコピーすることができます(コピー先に同名のファイルがある場合はコピー元のファイルで上書きされます)。

まず、dirs_exist_okを指定しない場合(またはFalseを指定した場合)のサンプルコードを以下に示します。

サンプルコード

import shutil

src_dir = 'testdir/dir1'
dst_dir = 'testdir/dir2'

shutil.copytree(src_dir, dst_dir)

エラーメッセージ

FileExistsError: [WinError 183] 既に存在するファイルを作成することはできません。: 'testdir/dir2'

dirs_exist_okTrueにすると、既存のディレクトリパス(存在するディレクトリのパス)にコピーすることができます。サンプルコードを以下に示します。

サンプルコード

import shutil

src_dir = 'testdir/dir1'
dst_dir = 'testdir/dir2'

# 第2引数dstに既存のディレクトリパスを指定してもエラーを発生させない
shutil.copytree(src_dir, dst_dir, dirs_exist_ok=True)

上記のサンプルコード実行後のディレクトリ構成

testdir/
├── dir1/
│   ├── subdir1/
│   │   └── file1.txt
│   └── file2.txt
└── dir2/
    ├── subdir1/
    │   └── file1.txt
    └── file2.txt

中間ディレクトリが既に存在するディレクトリの場合

以下に示すように第2引数dstに指定するディレクトリパスにおいて、中間ディレクトリが既に存在するディレクトリの場合は、エラーにならず、コピー元のディレクトリsrcの中身を丸ごと、dstの中にコピーすることができます。

サンプルコード

import shutil

src_dir = 'testdir/dir1'
dst_dir = 'testdir/dir2/dir1_copy' # dir2は既に存在するディレクトリ(中間ディレクトリ)

shutil.copytree(src_dir, dst_dir)

上記のサンプルコード実行後のディレクトリ構成

testdir/
├── dir1/
│   ├── subdir1/
│   │   └── file1.txt
│   └── file2.txt
└──  dir2/
    └── dir1_copy/
        ├── subdir1/
        │   └── file1.txt
        └── file2.txt

shutil.copytree()のオプション

shutil.copytree()に使用できる以下のオプションについて順番に詳しく説明します。

  • symlinks引数
  • ignore引数
  • copy_function引数
  • ignore_dangling_symlinks引数
  • dirs_exist_ok引数

symlinks引数

symlinksは、シンボリックリンクをコピーするかどうかを指定する引数です。デフォルトはFalseであり、この場合、シンボリックリンク自体をコピーせず、リンク先のファイルやディレクトリをコピーします。Trueを指定すると、シンボリックリンク自体をコピーします。

以下のディレクトリ構成とします(linkがシンボリックリンクであり、somefileを参照しています)。

testdir/
├── dir1/
│   ├── subdir/
│   │   └── file1.txt
│   ├── file2.txt
│   └── link -> ../somefile
└── dir2/

サンプルコード

import shutil

src_dir = 'testdir/dir1'
dst_dir = 'testdir/dir3'

shutil.copytree(src_dir, dst_dir, symlinks=True)

上記のサンプルコード実行後のディレクトリ構成

testdir/
├── dir1/
│   ├── subdir/
│   │   └── file1.txt
│   ├── file2.txt
│   └── link -> ../somefile
└── dir3/
    ├── subdir/
    │   └── file1.txt
    ├── file2.txt
    └── link -> ../somefile

シンボリックリンクlinkがコピーされていることが確認できます。symlinks=False(デフォルト設定)で実行した場合、リンク先のファイルがコピーされるので、リンク自体はコピーされません。

ignore引数

ignoreは「コピー対象外とするファイルを指定するための関数」を指定する引数です。この関数はディレクトリ内のファイル名のリストを受け取り、無視するファイル名のリストを返します。

以下のディレクトリ構成とします。

testdir/
├── dir1/
│   ├── subdir/
│   │   └── file1.txt
│   ├── file2.txt
│   └── file2.jpg
└── dir2/

以下のサンプルコードでは、.txtで終わるファイル(拡張子がtxtのファイル)をコピー対象外としています。

サンプルコード

import shutil

def ignore_files(dir, files):
    return [f for f in files if f.endswith('.txt')]

src_dir = 'testdir/dir1'
dst_dir = 'testdir/dir3'

shutil.copytree(src_dir, dst_dir, ignore=ignore_files)

上記のサンプルコード実行後のディレクトリ構成

testdir/
├── dir1/
│   ├── subdir/
│   │   └── file1.txt
│   ├── file2.txt
│   └── file2.jpg
├── dir2/
└── dir3/
    ├── subdir/
    └── file2.jpg

ignore_patterns()関数

ignore引数にはshutil.ignore_patterns()を指定することもできます。shutil.ignore_patterns()には、glob形式のパターンを複数指定します。指定したパターンに一致するファイルやディレクトリをコピー対象外とすることができます。

以下のディレクトリ構成とします。

testdir/
├── dir1/
│   ├── subdir/
│   │   └── file1.txt
│   ├── file2.txt
│   └── file2.jpg
└── dir2/

このサンプルコードでは、.txtで終わるファイル(拡張子がtxtのファイル)とsubdirをコピー対象外としています。

サンプルコード

import shutil

src_dir = 'testdir/dir1'
dst_dir = 'testdir/dir3'

shutil.copytree(src_dir, dst_dir, ignore=shutil.ignore_patterns('*.txt', 'subdir'))

上記のサンプルコード実行後のディレクトリ構成

testdir/
├── dir1/
│   ├── subdir/
│   │   └── file1.txt
│   ├── file2.txt
│   └── file2.jpg
├── dir2/
└── dir3/
    └── file2.jpg

copy_function引数

copy_functionは「ファイルをコピーするために使用する関数」を指定する引数です。デフォルトは shutil.copy2 であり、可能な限りメタデータもコピーします。メタデータをコピーしたくない場合には、copy_function引数でshutil.copy()を指定します。

サンプルコード

import shutil
import os

src_dir = 'testdir/dir1'
dst_dir = 'testdir/dir3'

# ファイル名を'file2.txt'に修正
shutil.copytree(src_dir, dst_dir, copy_function=shutil.copy)

print(os.path.getmtime('testdir/dir1/file2.txt') == os.path.getmtime('testdir/dir3/file2.txt'))
# False ← shutil.copy()の場合はコピーしたファイルの更新日時はコピーした時刻になるため

copy_function引数を指定しない場合には、shutil.copy2を用いてファイルがコピーされるため、可能な限りメタデータもコピーされ、コピーしたファイルの更新日時は元のファイルと同じになります。

サンプルコード

import shutil
import os

src_dir = 'testdir/dir1'
dst_dir = 'testdir/dir3'

# ファイル名を'file2.txt'に修正
shutil.copytree(src_dir, dst_dir)

# コピー元とコピー先の正しいパスを指定
print(os.path.getmtime('testdir/dir1/file2.txt') == os.path.getmtime('testdir/dir3/file2.txt'))
# True ← shutil.copy2()の場合はファイルの更新日時は元のファイルと同じであるため

あわせて読みたい

shutil.copy()shutil.copy2()については下記の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。

ignore_dangling_symlinks引数

ignore_dangling_symlinksはぶら下がりシンボリックリンクを無視するかどうかを指定する引数です。デフォルトは False です。なお、ぶら下がりシンボリックリンク(dangling symlink)は、リンク先が存在しないシンボリックリンクです。通常のシンボリックリンクは、リンク元がリンク先のファイルやディレクトリを参照しますが、リンク先が削除されたり移動された場合、シンボリックリンクがぶら下がり状態(dangling)になります。

以下のディレクトリ構成とします。broken_linkがぶら下がりシンボリックリンクです。

testdir/
├── dir1/
│   ├── subdir/
│   │   └── file1.txt
│   ├── file2.txt
│   └── broken_link -> non_existent_file
└── dir2/

以下のサンプルコードでは、ignore_dangling_symlinks=Trueを設定することで、ぶら下がりシンボリックリンクが存在しても、コピーが実行されるようにしています(ら下がりシンボリックリンクもそのままコピーされます)。Falseに設定されている場合、broken_linkのようなぶら下がりシンボリックリンクが存在するとエラーが発生します。

サンプルコード

import shutil

src_dir = 'testdir/dir1'
dst_dir = 'testdir/dir3'

shutil.copytree(src_dir, dst_dir, ignore_dangling_symlinks=True)

上記のサンプルコード実行後のディレクトリ構成

testdir/
├── dir1/
│   ├── subdir/
│   │   └── file1.txt
│   ├── file2.txt
│   └── broken_link -> non_existent_file
├── dir2/
└── dir3/
    ├── subdir/
    │   └── file1.txt
    ├── file2.txt
    └── broken_link -> non_existent_file

dirs_exist_ok引数

dirs_exist_okはコピー先ディレクトリが存在する場合にエラーを発生させないかどうかを指定する引数です。デフォルトはFalseです。デフォルトでは、コピー先ディレクトリが存在するとFileExistsErrorが発生します。Trueに設定すると、エラーが発生しなくなります。

サンプルコード

import shutil

src_dir = 'testdir/dir1'
dst_dir = 'testdir/dir2'

# 第2引数dstに既存のディレクトリパスを指定してもエラーを発生させない
shutil.copytree(src_dir, dst_dir, dirs_exist_ok=True)

本記事のまとめ

この記事ではPythonのディレクトリを丸ごとコピーする関数『shutil.copytree()』について、以下の内容を説明しました。

  • shutil.copytree()とは
  • shutil.copytree()の構文
  • shutil.copytree()の使い方
    • ディレクトリを新規のディレクトリにコピーする方法
    • ディレクトリを既存のディレクトリにコピーする方法
  • shutil.copytree()のオプション
    • symlinks引数
    • ignore引数
    • copy_function引数
    • ignore_dangling_symlinks引数
    • dirs_exist_ok引数

お読み頂きありがとうございました。