Python、shutilでファイル、ディレクトリ操作

Twitterでシェア FaceBookでシェア はてなブックマークでシェア

Python - 標準ライブラリ
2018年11月22日21:15に更新(約21日前)
2018年11月8日3:00に作成(約35日前)

旧ブログ移行記事です。

shutilとは

公式ドキュメントから抜粋します。

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

高水準の操作ということで、単純なopen()等を使うよりも幅広く、様々なことができます。

ファイルのコピー

以下は、a.pyというファイルをa2.pyというファイル名でコピーして作成します。 既にa2.pyが存在していた場合は、上書きします。

import shutil

# (コピー元, コピー先)
shutil.copyfile('a.py', 'a2.py')

コピー先がディレクトリの場合は、copyfileではなくcopyを使います。

import shutil

shutil.copy('a.py', 'folderA')

この場合、folderAの中にa.pyがコピーされます。 通常のファイル→ファイルへのコピーもできるので、とりあえずこのcopy()を使っておけば間違いないです。

ファイルっぽいオブジェクトのコピー

copyfileobjという、ファイルではなくファイル形式のオブジェクトを扱う機能もあります。 以下は、Helloと書かれたfile.pyが出来上がります。

import shutil
from io import StringIO

fsrc = StringIO('Hello')
with open('file.py', 'w') as fdst:
    shutil.copyfileobj(fsrc, fdst)

ByteIOバージョンはこうです。

import shutil
from io import BytesIO

fsrc = BytesIO(b'Hello')
with open('file.py', 'wb') as fdst:
    shutil.copyfileobj(fsrc, fdst)

Python、requestsを使ったダウンロードでも紹介しましたが、ファイルのダウンロード等にもcopyfileobjは使えます。

ディレクトリをまるごとコピー

copytreeを使うと、ディレクトリを丸ごとコピーできます。

import shutil

shutil.copytree('folderA', 'folderC')

ディレクトリの削除

rmtreeを使うと、ディレクトリ丸ごと削除できます。

import shutil

shutil.rmtree('folderC')

ファイルの削除

単一のファイルならば、os.removeを使います。

import os

os.remove('file.txt')

ファイル・ディレクトリの移動、リネーム

moveは、ファイル又はディレクトリを別の場所に移動します。また、リネームにも使えます。

移動先がディレクトリとして存在していれば、元srcがファイルでもディレクトリでも、移動先のディレクトリの中に入れてくれます。

移動先がファイルとして存在していた場合、元srcがディレクトリの指定ならばエラー。元srcがファイルならば上書きします。

Unixにおけるmvコマンドと大体同じと言ったほうが分かりやすいでしょうか。

import shutil

shutil.move('a.py', 'b.py')
shutil.move('folderA', 'folderB')

ZIPの作成

zipなどのアーカイブ化操作も可能です。 以下は、カレントディレクトリのZIPが作成されます。current.zipです。

import shutil

shutil.make_archive('current', 'zip')

ちょっと便利なZIP

カレントディレクトリに、与えられたパスのzipを作成したい、という処理を考えてみます。

まず、このようなディレクトリの構成です。main.pyが、作成するファイルになります。

Aの中は、ファイルが2つ

Bの中は、ディレクトリとファイルが1つ

更にCは、ファイルが2つあります。

以下のように、相対パスか絶対パスで指定すると...

    make('A')
    make(r'C:\MyMercurial\test\testpython\shu\B')
    make('B\C')

カレントにA、B、Cそれぞれのアーカイブができて

中身も、それぞれ大丈夫そうですね。 A.zipの中身

B.zipの中身

C.zipの中身

このような動作をする関数を作成してみました。

import os
import shutil


def make(target, kind='zip'):
    """与えられたファイル・ディレクトリを圧縮する

    引数:
        target: 圧縮したいファイル・ディレクトリ。相対、フル、どちらもOK
        kind: 圧縮の種類。デフォルトでzip
    """

    current = os.getcwd()
    head, tail = os.path.split(target)
    base_name = os.path.join(current, tail)
    root_dir = os.path.join(current, head)
    base_dir = tail

    shutil.make_archive(
        base_name, kind, root_dir=root_dir, base_dir=base_dir
    )


if __name__ == '__main__':
    make('A')
    make(r'C:\MyMercurial\test\testpython\shu\B')
    make('B\C')

個人的には、なかなか便利ではないかなぁと思っています。

Twitterでシェア FaceBookでシェア はてなブックマークでシェア

記事にコメントする