Pythonでファイルまたはディレクトリを再帰的にコピーする


116

Pythonには、ファイルをコピーするための関数(などshutil.copy)とディレクトリをコピーするための関数(たとえばshutil.copytree)があるようですが、両方を処理する関数は見つかりませんでした。確かに、ファイルとディレクトリのどちらをコピーするかを確認するのは簡単ですが、奇妙な省略のようです。

unix cp -rコマンドのように機能する、つまり、ディレクトリとファイルの両方をサポートし、再帰的にコピーする標準関数は本当にありませんか?Pythonでこの問題を回避する最もエレガントな方法は何でしょうか?


3
はい、これは混乱です。基礎となるシステムコールを反映しようとすることにより、Pythonが目に見えるインターフェースを悪化させる場所の1つ。copy-fileとcopy-treeを切り替えることは難しくありませんが、必要ではありませんでした。たぶんcopytree、単一のファイルをコピーできるようにするために、Pythonバグトラッカーに拡張リクエストを提出しますか?
ボビンス、2010年

回答:


142

最初にを呼び出しshutil.copytree、例外がスローされた場合はで再試行することをお勧めしますshutil.copy

import shutil, errno

def copyanything(src, dst):
    try:
        shutil.copytree(src, dst)
    except OSError as exc: # python >2.5
        if exc.errno == errno.ENOTDIR:
            shutil.copy(src, dst)
        else: raise

18
このような例外をキャッチする代わりに、srcがos.path.isdir(src)を使用してディレクトリであるかどうかを単純にチェックする方がはるかにクリーンだと思います。または、代わりにここで例外を使用する必要がある特別な理由がありますか?
pafcu

31
1)Pythonの世界では、EAFP(許可よりも許しを求める方が簡単です)がLBYLよりも優先されます(跳躍する前に注意してください)。それがあなたに新しく聞こえるなら、私はそれについてのリンクをあなたに提供することができます。2)ライブラリ関数はすでに間接的にそれをチェックしていますが、なぜチェックを複製するのですか?3)shutil.copytree将来、両方のケースを改善および管理する機能が停止するものはありません。4)例外はPythonではそれほど例外ではありません。たとえば、反復はStopIteration例外をスローすることで停止します。
tzot

2
この場合、例外の処理には6行かかりますが、型のチェックには4行かかります。それほどではありませんが、最終的には加算されます。また、あなたが言うように、copytreeはいつの日かファイルをサポートするかもしれません。しかし、その実装がどのようになるかを知ることは不可能です。多分それはコピーが機能するいくつかの状況下で例外をスローしますか?その場合、機能が追加されただけでコードが突然機能しなくなります。しかし、あなたはおそらく正しい、例外はPythonでかなり一般的であり、これは私が非常に不快に思うものですが、おそらく私はそれに慣れていないように思われるためです
pafcu

5
実際には、この場合の例外には明確な客観的な利点が1つあります。チェックと正しい関数の呼び出しとの間で型が変わる可能性は(非常に低いですが)可能です。
10

2
私の個人的な意見では、exceptにコア機能を追加することは悪い習慣であり、どの言語を使用しているかは関係ありません。多くの開発者が検索しない場所に機能を配置します。さらに、コメントを書かなければ、経験の浅いPython開発者は、この再試行の目的が何であるかを実際には理解できません。このような些細なことについてコメントを追加する必要がある場合は、コードスタイルに問題があります。最後に、if / elseを記述すると、コードが非常に読みやすくなります。
this.myself

7

追加するにはTzotさんGNS答え、ここで再帰的にファイルやフォルダをコピーする別の方法です。(Python 3.X)

import os, shutil

root_src_dir = r'C:\MyMusic'    #Path/Location of the source directory
root_dst_dir = 'D:MusicBackUp'  #Path to the destination folder

for src_dir, dirs, files in os.walk(root_src_dir):
    dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1)
    if not os.path.exists(dst_dir):
        os.makedirs(dst_dir)
    for file_ in files:
        src_file = os.path.join(src_dir, file_)
        dst_file = os.path.join(dst_dir, file_)
        if os.path.exists(dst_file):
            os.remove(dst_file)
        shutil.copy(src_file, dst_dir)

初めてで、ファイルやフォルダを再帰的にコピーする方法がわからない場合は、これが役立つことを願っています。


3

shutil.copyそしてshutil.copy2、ファイルをコピーしています。

shutil.copytreeすべてのファイルとすべてのサブフォルダーを含むフォルダーをコピーします。shutil.copytree使用していますshutil.copy2ファイルのコピーにしています。

だからcp -rあなたに言っているのは、フォルダとそのファイル/サブフォルダをターゲットにしてコピーするshutil.copytreeからです。コピーをせずにファイルを実行します。cp -rshutil.copytree-r cpshutil.copyshutil.copy2


1
あなたはその質問を理解していないと思います。お試しくださいshutil.copytree('C:\myfile.txt', 'C:\otherfile')。動かない。それがOPが求めていたものです... 7年前。
ジャン=フランソワ・コルベット

もちろん動作しません。cpと同様に、フォルダーでは機能しません。特別なオプションが必要です。copyとcopytreeは、コピーを処理するための最良の方法です。copytreeがファイルをターゲットにできる場合、ミスを犯しやすくなります。LinuxとPythonの両方でターゲットとするものを知っている必要があります。難しい。さらに、他の誰かがここでコメントしましたが、質問と返信を見ただけでは答えられませんでした。エレガントな方法は、何をしたいのかを知ることであり、コントロールなしのユニバーサルコピーではありません。
gms

2

Unix cpは「ディレクトリとファイルの両方をサポートしていません」:

betelgeuse:tmp james$ cp source/ dest/
cp: source/ is a directory (not copied).

cpにディレクトリをコピーさせるには、「-r」フラグを使用して、それがディレクトリであることを手動でcpに通知する必要があります。

ただし、ここではいくつかの切断があります- cp -rソースとしてファイル名が渡されると、単一のファイルだけが喜んでコピーされます。copytreeはしません。


2
docs.python.org/library/shutil.htmlには、通常のファイル、シンボリックリンク、およびディレクトリの処理を示すcopytree()のコードが含まれています。
James Polley、2010年

1
この回答は質問には対応していません。回答ではなくコメントにする必要があります。
ジャン=フランソワ・コルベット


-2

pythonのshutil.copytreeメソッドはめちゃくちゃです。私は正しく機能するものを実行しました:

def copydirectorykut(src, dst):
    os.chdir(dst)
    list=os.listdir(src)
    nom= src+'.txt'
    fitx= open(nom, 'w')

    for item in list:
        fitx.write("%s\n" % item)
    fitx.close()

    f = open(nom,'r')
    for line in f.readlines():
        if "." in line:
            shutil.copy(src+'/'+line[:-1],dst+'/'+line[:-1])
        else:
            if not os.path.exists(dst+'/'+line[:-1]):
                os.makedirs(dst+'/'+line[:-1])
                copydirectorykut(src+'/'+line[:-1],dst+'/'+line[:-1])
            copydirectorykut(src+'/'+line[:-1],dst+'/'+line[:-1])
    f.close()
    os.remove(nom)
    os.chdir('..')

1
このコードは、個々のファイルのチェック(上書きの問題のチェック)には適していますが、「zip」などのバイナリファイルでは機能しません。行ごとの読み書きの代わりに単純なPythonファイルのコピーを使用しないのはなぜですか?
notilas
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.