【Python】CSVに書かれたファイルを一括でコピーする

python

Pythonのファイル操作についてです。

ディレクトリの中から複数の特定ファイルをコピーする方法として、CSVファイルを使ったファイルの一括コピーを実行するスクリプトを作成しました。

コピーしたいファイルのリストを予め準備しておくことで、何度も同じファイルを簡単にコピーすることができるため、ファイルが度々更新するようなものを繰り返し取得したい場合に便利です。

では、参考プログラムと手順について説明します。

【準備】CSVファイルを準備する

まずはコピーしたいファイル名を1行ずつ書いたCSVファイルを用意します。

全て手作業で準備するのが大変であれば、次のように一括でファイル名をCSVに出力して、不要なファイル名を削除するというやり方もあります。

Pythonでファイル名一覧をCSV出力する方法

os.listdir() でファイル名の一覧を取得して、csvモジュールでcsvに書き込みを行います。

import os
import csv
input_dir = './test'     # コピー元のディレクトリパス
csv_file = './list.csv'  # ファイル名一覧のcsvファイル
file_list = os.listdir(input_dir)  # ファイル名一覧取得
with open(csv_file, "w", newline="", encoding="utf-8") as f: # CSVファイルを書き込みモードで開く
    writer = csv.writer(f)
    for file in file_list:
        writer.writerow([file])  # 1行ずつファイル名を書き込み

Windosのコマンドでファイル名一覧をCSV出力する方法

もし、Windosでディレクトリの中のファイル名一覧を取得したいのであれば、コマンドプロンプトで目的のディレクトリに移動して、以下のコマンドを実行することでファイル名の一覧が取得できます。

dir /b *.txt > list.csv

【実行】CSVからファイル一覧を読み込みコピーする

ディレクトリに上のようなファイルがあったとして、

スクリプトの手順としては、

  1. ファイル元のディレクトリパスを指定 (./test ディレクトリ)
  2. コピー先のディレクトリパスを指定 (./output とします)
  3. CSVファイルパスを指定 (./list.csv)
  4. CSVファイルから1行ずつファイル名を読み込む
  5. 1のディレクトリから該当するファイルを2のディレクトリにコピーする
  6. 4~5を繰り返す

です。

CSVファイルの読み込みにはcsvモジュールを使い、ファイルのコピーには shutilモジュールを使います。なお、shutil.copy2() はファイルのメタ情報(作成日時など)も含めたコピーです。shutil.copy()にすると、新しい日時で作成されます。また、shutil.move()を使えばファイルを移動できます。

サンプルコード

import os
import shutil
import csv

input_dir = './test'     # コピー元のディレクトリパス
output_dir = './output'  # コピー先のディレクトリパス
csv_file = './list.csv'  # ファイル名一覧のcsvファイル

if not os.path.exists(output_dir):  
    os.makedirs(output_dir)  # ディレクトリがない場合、空のディレクトリを生成

with open(csv_file, encoding="utf-8") as f: # csvファイルを開く
    reader = csv.reader(f)  # csv読込
    for row in reader:  # 1行ずつ取得
        target_file = os.path.join(input_dir, row[0])  # コピーするファイルパスを生成
        print(row[0])
        if os.path.exists(target_file):
            shutil.copy2(target_file, output_dir)  # ファイルコピー

<結果>

【応用】コマンドでディレクトリを指定できるようにする

プログラムの中に入力ディレクトリや出力ディレクトリのパスを固定で入れると、パスが変わるたびにプログラムを修正する必要がありますので、可変値は実行時にコマンドで指定できるようにします。

こうすることで、パスが変わった場合でもプログラムを修正することなく、実行時のコマンド引数を変更するだけですみます。引数の取得には argparseモジュールを使います。

サンプルコード

import os
import shutil
import csv
import argparse

def get_params():
    parser = argparse.ArgumentParser(description='file copy')
    parser.add_argument("-i", "--input_dir", type=str, required=True, help='input_dir')
    parser.add_argument("-o", "--output_dir", type=str, default="./output", help='output_dir')
    parser.add_argument("-c", "--csv_file", type=str, required=True, help='csv_file')
    parser.add_argument("-m", "--move", action='store_true', help='move')
    args = parser.parse_args()
    return args

def main():
    params = get_params()  # コマンド実行時の引数取得
    input_dir = params.input_dir  # コピー元のディレクトリパス
    output_dir = params.output_dir  # コピー先のディレクトリパス
    csv_file = params.csv_file  # ファイル名一覧のcsvファイル
    is_move = params.move  # moveオプション

    if not os.path.exists(output_dir):
        os.makedirs(output_dir)  # ディレクトリがない場合、空のディレクトリを生成

    with open(csv_file, encoding="utf-8") as f: # csvファイルを開く
        reader = csv.reader(f)  # csv読込
        for row in reader:  # 1行ずつ取得
            target_file = os.path.join(input_dir, row[0])  # コピーするファイルパスを生成
            print(row[0])
            if os.path.exists(target_file):
                if is_move:
                    shutil.move(target_file, output_dir)  # ファイル移動
                else:
                    shutil.copy2(target_file, output_dir)  # ファイルコピー

if __name__ == '__main__':
    main()

<実行時のコマンド(ファイルコピー)>

python ./file_copy.py -i ./test/ -o ./output/ -c ./list.csv

<実行時のコマンド(ファイル移動)>

python ./file_copy.py -i ./test/ -o ./output/ -c ./list.csv -m

※ファイルを移動したい場合のオプション -m を追加しました

タイトルとURLをコピーしました