Pythonでディレクトリ内部のファイルを上書きせずに、存在しないファイルだけコピーしたい


以下のようなディレクトリ構造があります。

[code]

    testFolderA
        |_ copyfolder
            |_ aaa.txt
            |_ bbb.txt
            |_ ccc.txt
            |_ ddd.txt
            |_ eee.txt
    testFolderB
        |_ copyfolder
            |_ aaa.txt
            |_ ccc.txt
            |_ eee.txt
    
[/code]

Pythonで、copyfolderをディレクトリごとtestFolderAからtestFolderBにコピーしたいです。  
その際に、すでにあるファイルは上書きしないようにしたいです。

この例の場合だと、aaa.txtとccc.txtとeee.txtは既にあるのでコピーをせずに  
bbb.txtとddd.txtだけコピーしたいです。

以下のコマンドを実行したところ、エラーになってコピーできません。

[code]

    import shutil
    shutil.copytree('testFolderA/copyfolder','testFolderB/copyfolder')
    
[/code]

どのようなコードでコピーするのが効率的でしょうか？

さらに下位ディレクトリがある場合も、再帰的に処理を行い、すべてのファイルを走査し、ないものはコピー、あるものは保留をしたいです。コピー先にディレクトリがない場合はディレクトリも作成されていてほしいです。

Windows環境を想定しています。




python 3.8
で[`shutil.copytree`](https://docs.python.org/ja/3/library/shutil.html#shutil.copytree)の引数に`dirs_exist_ok`が追加されました。  
`dirs_exist_ok=True`を指定することでエラーが発生しなくなります。

ただしこの状態ではすべてのファイルを上書きしてしまうため、`ignore`引数を使用することで存在しないファイルのみコピーすることができます。  
実際のコードは下記のサンプルコードを参考にしてください。

**サンプルコード**

[code]

    import shutil
    import os
    
    def ignore(d, l):
        retVal = [] # 存在しないファイルのリスト
        for p in l:
            # isfile ではなく exists を使うとフォルダを再帰的にコピーしてくれないので注意
            if os.path.isfile(os.path.join(dst, p)): # ignore(d, l)のdはコピー元のパスなので、dstを使わないとコピー先の存在チェックができない
                retVal.append(p)
        # 存在しないファイルを無視リストとして戻す
        return retVal
    
    src = 'testFolderA/copyfolder'
    dst = 'testFolderB/copyfolder'
    # dirs_exist_okは3.8以降使用可能
    shutil.copytree(src, dst, ignore=ignore, dirs_exist_ok=True)
    
[/code]

Windows限定ならば`Process`からrobocopyなどのコマンドを呼び出す方法も考えられますが、この回答には含みません。




python 3.8
で[`shutil.copytree`](https://docs.python.org/ja/3/library/shutil.html#shutil.copytree)の引数に`dirs_exist_ok`が追加されました。  
`dirs_exist_ok=True`を指定することでエラーが発生しなくなります。

ただしこの状態ではすべてのファイルを上書きしてしまうため、`ignore`引数を使用することで存在しないファイルのみコピーすることができます。  
実際のコードは下記のサンプルコードを参考にしてください。

**サンプルコード**

[code]

    import shutil
    import os
    
    def ignore(d, l):
        retVal = [] # 存在しないファイルのリスト
        for p in l:
            # isfile ではなく exists を使うとフォルダを再帰的にコピーしてくれないので注意
            if os.path.isfile(os.path.join(dst, p)): # ignore(d, l)のdはコピー元のパスなので、dstを使わないとコピー先の存在チェックができない
                retVal.append(p)
        # 存在しないファイルを無視リストとして戻す
        return retVal
    
    src = 'testFolderA/copyfolder'
    dst = 'testFolderB/copyfolder'
    # dirs_exist_okは3.8以降使用可能
    shutil.copytree(src, dst, ignore=ignore, dirs_exist_ok=True)
    
[/code]

Windows限定ならば`Process`からrobocopyなどのコマンドを呼び出す方法も考えられますが、この回答には含みません。

