# 10.10. `shutil` --- 高水準のファイル操作

ソースコード: Lib/shutil.py

`shutil` モジュールはファイルやファイルの集まりに対する高水準の操作方法を多数提供します。特にファイルのコピーや削除のための関数が用意されています。個別のファイルに対する操作については、 `os` モジュールも参照してください。

警告

高レベルなファイルコピー関数 (`shutil.copy()`, `shutil.copy2()`) でも、全てのファイルのメタデータをコピーできるわけではありません。

POSIXプラットフォームでは、これはACLやファイルのオーナー、グループが失われることを意味しています。 Mac OSでは、リソースフォーク(resource fork)やその他のメタデータが利用されません。これは、リソースが失われ、ファイルタイプや生成者コード(creator code)が正しくなくなることを意味しています。 Windowsでは、ファイルオーナー、ACL、代替データストリームがコピーされません。

## 10.10.1. ディレクトリとファイルの操作

`shutil.``copyfileobj`(fsrc, fdst[, length])

  ファイル形式のオブジェクト fsrc の内容を fdst へコピーします。整数値 length は与えられた場合バッファサイズを表します。特に length が負の場合、チャンク内のソースデータを繰り返し操作することなくデータをコピーします。デフォルトでは、制御不能なメモリ消費を避けるためにデータはチャンク内に読み込まれます。 fsrc オブジェクトの現在のファイル位置が0でない場合、現在の位置からファイル終端までの内容のみがコピーされることに注意してください。

`shutil.``copyfile`(src, dst)

  src で指定されたファイルの内容を dst で指定されたファイルへとコピーします。(メタデータはコピーされません) dst は完全なターゲットファイル名である必要があります。コピー先にディレクトリ名を使用したい場合は、 `shutil.copy()` を参照してください。もし、 src と dst が同じファイルであれば、 `Error` 例外が発生します。コピー先は書き込み可能である必要があります。そうでなければ `IOError` を発生します。もし dst が存在したら、置き換えられます。キャラクタやブロックデバイス、パイプ等の特別なファイルはこの関数ではコピーできません。 src と dst にはパス名を文字列で与えられます。

`shutil.``copymode`(src, dst)

  src から dst へパーミッションをコピーします。ファイル内容や所有者、グループは影響を受けません。 src と dst には文字列としてパス名を与えられます。

`shutil.``copystat`(src, dst)

  src から dst へ、パーミッション、最終アクセス時間、最終更新時間、フラグをコピーします。ファイル内容や所有者、グループは影響を受けません。 src と dst には文字列としてパス名を与えられます。

`shutil.``copy`(src, dst)

  ファイル src をファイルまたはディレクトリ dist へコピーします。もし、 dst がディレクトリであればファイル名は src と同じものが指定されたディレクトリ内に作成（または上書き）されます。パーミッションはコピーされます。 src と dst には文字列としてパス名を与えられます。

`shutil.``copy2`(src, dst)

  Identical to `copy()` except that `copy2()` also attempts to preserve file metadata.

  `copy2()` uses `copystat()` to copy the file metadata. Please see `copystat()` for more information.

`shutil.``ignore_patterns`(*patterns)

  このファクトリ関数は、 `copytree()` 関数の ignore 引数に渡すための呼び出し可能オブジェクトを作成します。 glob形式の patterns にマッチするファイルやディレクトリが無視されます。下の例を参照してください。

  バージョン 2.6 で追加.

`shutil.``copytree`(src, dst, symlinks=False, ignore=None)

  src を起点としたディレクトリツリーをコピーします。 dst で指定されたターゲットディレクトリは、既存のもので無い必要があります。存在しない親ディレクトリも含めて作成されます。パーミッションと時刻は `copystat()` 関数でコピーされます。個々のファイルは `shutil.copy2()` によってコピーされます。

  symlinks が真であれば、元のディレクトリ内のシンボリックリンクはコピー先のディレクトリ内へシンボリックリンクとしてコピーされます。偽が与えられたり省略された場合は元のディレクトリ内のリンクの対象となっているファイルがコピー先のディレクトリ内へコピーされます。

  ignore は `copytree()` が走査しているディレクトリと `os.listdir()` が返すその内容のリストを引数として受け取ることのできる呼び出し可能オブジェクトでなければなりません。 `copytree()` は再帰的に呼び出されるので、 ignore はコピーされる各ディレクトリ毎に呼び出されます。 ignore の戻り値はカレントディレクトリに相対的なディレクトリ名およびファイル名のシーケンス（すなわち第二引数の項目のサブセット）でなければなりません。それらの名前はコピー中に無視されます。 `ignore_patterns()` を用いて glob 形式のパターンによって無視する呼び出し可能オブジェクトを作成することが出来ます。

  例外が発生した場合、理由のリストとともに `Error` を送出します。

  この関数は、究極の道具としてではなく、ソースコードが利用例になっていると捉えるべきでしょう。

  バージョン 2.3 で変更: コピー中にエラーが発生した場合、メッセージを出力するのではなく `Error` を起こす。

  バージョン 2.5 で変更: dst を作成する際に中間のディレクトリ作成が必要な場合、エラーを起こすのではなく作成する。ディレクトリのパーミッションと時刻を `copystat()` を利用してコピーする。

  バージョン 2.6 で変更: 何がコピーされるかを制御するための ignore 引数

`shutil.``rmtree`(path[, ignore_errors[, onerror]])

  ディレクトリツリー全体を削除します。 path はディレクトリを指しているなければなりません（ただしディレクトリに対するシンボリックリンクではいけません）。もし ignore_errors が真であれば削除に失敗したことによるエラーは無視されます。偽や省略された場合はこれらのエラーは onerror で与えられたハンドラを呼び出して処理され、 onerror が省略された場合は例外を送出します。

  onerror が与えられた場合、それは3つのパラメータ function, path および excinfo を受け入れて呼び出し可能のものでなくてはなりません。最初のパラメータ function は例外を引き起こした関数で `os.listdir()`, `os.remove()`, `os.rmdir()` のいずれかでしょう。 2番目のパラメータ path は function へ渡されたパス名です。 3番目のパラメータ excinfo は `sys.exc_info()` で返されるような例外情報になるでしょう。 onerror が引き起こす例外はキャッチできません。

  バージョン 2.6 で変更: path を明示的にチェックして、シンボリックリンクだった場合は `OSError` を返すようになりました。

`shutil.``move`(src, dst)

  再帰的にファイルやディレクトリ (src) を別の場所 (dst) へ移動します。

  移動先が存在するディレクトリの場合、 src はそのディレクトリの中へ移動します。移動先が存在していてそれがディレクトリでない場合、 `os.rename()` の動作によっては上書きされることがあります。

  もし移動先が現在のファイルシステム上であれば `os.rename()` を使います。そうでない場合は src を(`shutil.copy2()` で) dst にコピーし、その後コピー元は削除されます。

  バージョン 2.3 で追加.

exception `shutil.``Error`

  この例外は複数ファイルの操作を行っているときに生じる例外をまとめたものです。 `copytree()` に対しては例外の引数は3つのタプル(srcname, dstname, exception)からなるリストです。

  バージョン 2.3 で追加.

### 10.10.1.1. copytree の例

以下は前述の `copytree()` 関数のドキュメント文字列を省略した実装例です。本モジュールで提供される他の関数の使い方を示しています。

    def copytree(src, dst, symlinks=False, ignore=None):
        names = os.listdir(src)
        if ignore is not None:
            ignored_names = ignore(src, names)
        else:
            ignored_names = set()

        os.makedirs(dst)
        errors = []
        for name in names:
            if name in ignored_names:
                continue
            srcname = os.path.join(src, name)
            dstname = os.path.join(dst, name)
            try:
                if symlinks and os.path.islink(srcname):
                    linkto = os.readlink(srcname)
                    os.symlink(linkto, dstname)
                elif os.path.isdir(srcname):
                    copytree(srcname, dstname, symlinks, ignore)
                else:
                    copy2(srcname, dstname)
                # XXX What about devices, sockets etc.?
            except (IOError, os.error) as why:
                errors.append((srcname, dstname, str(why)))
            # catch the Error from the recursive copytree so that we can
            # continue with other files
            except Error as err:
                errors.extend(err.args[0])
        try:
            copystat(src, dst)
        except WindowsError:
            # can't copy file access times on Windows
            pass
        except OSError as why:
            errors.extend((src, dst, str(why)))
        if errors:
            raise Error(errors)

`ignore_patterns()` ヘルパ関数を利用する、もう1つの例です。

    from shutil import copytree, ignore_patterns

    copytree(source, destination, ignore=ignore_patterns('*.pyc', 'tmp*'))

この例では、 `.pyc` ファイルと、 `tmp` で始まる全てのファイルやディレクトリを除いて、全てをコピーします。

ignore 引数にロギングさせる別の例です。

    from shutil import copytree
    import logging

    def _logpath(path, names):
        logging.info('Working in %s' % path)
        return []   # nothing will be ignored

    copytree(source, destination, ignore=_logpath)

## 10.10.2. アーカイブ化操作

圧縮とアーカイブ化されているファイルの読み書きの高水準なユーティリティも提供されています。これらは `zipfile` 、 `tarfile` モジュールに依拠しています。

`shutil.``make_archive`(base_name, format[, root_dir[, base_dir[, verbose[, dry_run[, owner[, group[, logger]]]]]]])

  アーカイブファイル (zip や tar など) を作成し、その名前を返します。

  base_name は作成するファイルの名前で、パスを含み、フォーマットごとの拡張子を抜いたものです。 format はアーカイブフォーマットで "zip" (`zlib` モジュールもしくは外部の `zip` 実行可能ファイルが利用可能な場合), "tar", "gztar" (`zlib` モジュールが利用可能な場合), "bztar" (`bz2` モジュールが利用可能な場合) のいずれかです。

  root_dir は、アーカイブのルートディレクトリとなるディレクトリです。すなわち、一般的には、アーカイブを作成する前に root_dir をカレントディレクトリにします。

  base_dir は、アーカイブを開始するディレクトリです。すなわち、 base_dir は、アーカイブのすべてのファイルとディレクトリに共通する接頭辞になります。

  root_dir と base_dir のどちらも、デフォルトはカレントディレクトリです。

  owner と group は、tar アーカイブを作成するときに使われます。デフォルトでは、カレントのオーナとグループを使います。

  logger は PEP 282 に互換なオブジェクトでなければなりません。これは普通は `logging.Logger` のインスタンスです。

  バージョン 2.7 で追加.

`shutil.``get_archive_formats`()

  アーカイブ化をサポートしているフォーマットのリストを返します。返されるシーケンスのそれぞれの要素は、タプル `(name, description)` です。

  デフォルトでは、 `shutil` はこれらのフォーマットを提供しています:

  - zip: ZIP ファイル (`zlib` モジュールもしくは外部の `zip` 実行可能ファイルが利用可能な場合)。
  - tar: 圧縮されていない tar ファイル。
  - gztar: gzip で圧縮された tar ファイル (`zlib` モジュールが利用可能な場合)。
  - bztar: bzip2 で圧縮された tar ファイル (`bz2` モジュールが利用可能な場合)。

  `register_archive_format()` を使って、新しいフォーマットを登録したり、既存のフォーマットに独自のアーカイバを提供したりできます。

  バージョン 2.7 で追加.

`shutil.``register_archive_format`(name, function[, extra_args[, description]])

  フォーマット name のアーカイバを登録します。 function は、アーカイバを呼び出すのに使われる呼び出し可能オブジェクトです。

  extra_args は、与えられるなら `(name, value)` のシーケンスで、アーカイバ呼び出し可能オブジェクトが使われるときの追加キーワード引数に使われます。

  description は、アーカイバのリストを返す `get_archive_formats()` で使われます。デフォルトでは、空のリストです。

  バージョン 2.7 で追加.

`shutil.``unregister_archive_format`(name)

  アーカイブフォーマット name を、サポートされているフォーマットのリストから取り除きます。

  バージョン 2.7 で追加.

### 10.10.2.1. アーカイブ化の例

この例では、ユーザの `.ssh` ディレクトリにあるすべてのファイルを含む、 gzip された tar ファイルアーカイブを作成します:

    >>> from shutil import make_archive
    >>> import os
    >>> archive_name = os.path.expanduser(os.path.join('~', 'myarchive'))
    >>> root_dir = os.path.expanduser(os.path.join('~', '.ssh'))
    >>> make_archive(archive_name, 'gztar', root_dir)
    '/Users/tarek/myarchive.tar.gz'

結果のアーカイブは、以下のものを含みます:

    $ tar -tzvf /Users/tarek/myarchive.tar.gz
    drwx------ tarek/staff       0 2010-02-01 16:23:40 ./
    -rw-r--r-- tarek/staff     609 2008-06-09 13:26:54 ./authorized_keys
    -rwxr-xr-x tarek/staff      65 2008-06-09 13:26:54 ./config
    -rwx------ tarek/staff     668 2008-06-09 13:26:54 ./id_dsa
    -rwxr-xr-x tarek/staff     609 2008-06-09 13:26:54 ./id_dsa.pub
    -rw------- tarek/staff    1675 2008-06-09 13:26:54 ./id_rsa
    -rw-r--r-- tarek/staff     397 2008-06-09 13:26:54 ./id_rsa.pub
    -rw-r--r-- tarek/staff   37192 2010-02-06 18:23:10 ./known_hosts