Lambda

最新n個のファイルをコピーする

Windowsで,あるディレクトリにあるファイルを,新しい順にn個,別のディレクトリにコピーしたい.GUIでやるならエクスプローラからファイルを日付でソートし,上からn個選んでコピーすれば良いのだけれど,自動でやろうとすると少し面倒である.

バッチファイルだけでは荷が重そうなので,Pythonを使う.

  • Windows 10
  • Python 3

ディレクトリ構成

以下のような構成で,ファイルの新旧は(古い←) a b c d e f (→新しい)とする.C:\srcから新しい順に3つf,e,dをC:/destにコピーしたい.

C:/
├ src/
│ ├ dir1
│ │ ├ b.txt
│ │ ├ d.txt
│ │ └ f.txt
│ ├ a.txt
│ ├ c.txt
│ └ e.txt
└ dest/

特定のディレクトリにあるファイルの一覧

特定のディレクトリにあるファイルの一覧は,pathlibで取得できる.

import pathlib

path_name_from = "C:\src"

# srcディレクトリのPathオブジェクト
path = pathlib.Path(src_path_name)
assert path.is_dir()  # 指定されたパスがディレクトリではない時はエラー

# path以下の全てのファイルとディレクトリ
files = list(path.glob("**/*"))

for f in files:
  print(f)

出力は以下の通り.

C:\src\a.txt
C:\src\c.txt
C:\src\dir1
C:\src\e.txt
C:\src\dir1\b.txt
C:\src\dir1\d.txt
C:\src\dir1\f.txt

ファイルを抽出

取得したfilesは,ファイルとディレクトリが混じった状態になっている.ここから,ファイルだけを抽出する.

# 指定したパスがファイルかどうか判定する関数
def is_file(path):
    return path.is_file()

# is_file()が真のパスのみ抽出
files = list(filter(is_file, files))

for f in files:
  print(f)

出力は以下の通り.

C:\src\a.txt
C:\src\c.txt
C:\src\e.txt
C:\src\dir1\b.txt
C:\src\dir1\d.txt
C:\src\dir1\f.txt

作成日時によるソート

Path.stat()os.stat_resultが取得でき,そこから以下の情報を参照できる.

  • st_atime: 最終アクセス時刻 (sec)
  • st_mtime: 最終内容更新時刻 (sec)
  • st_ctime: 作成時刻 (sec)

これを用いて,filesを最終内容更新時刻でソートする.

def modified_time(path):
    return path.stat().st_mtime

# 更新日時でソート
files.sort(key=modified_time, reverse=True)

for f in files:
  print(f)

新しい順に使いたいので,降順ソート(reverse)しておいた.出力は以下の通り.

C:\src\dir1\f.txt
C:\src\e.txt
C:\src\dir1\d.txt
C:\src\c.txt
C:\src\dir1\b.txt
C:\src\a.txt

copyコマンド

Python上から,Windowsのcopyコマンドを実行する.ここでは,subprocess.run()を使う.

import subprocess

path_name_to = "C:\dest"
N_MAX_COPY = 3
n_copy = min(N_MAX_COPY, len(files))  # コピーするファイルの数は,コピーしたいフ
                                      # ァイルの数とディレクトリに存在するファイ
                                      # ルの数の小さい方.

for i in range(n_copy):
    subprocess.run(
        "copy" + 
        " \"" + str(files[i]) + "\"" +
        " \"" + path_name_to + "\"", shell=True)

最新n個のファイルをコピーするスクリプト

以上をまとめると,path_name_fromからpath_name_toに最新n個のファイルをコピーするには以下のようにする.

import pathlib
import subprocess


def main():
    path_name_from = "C:\\src"
    path_name_to = "C:\\dest"
    N_MAX_COPY = 3
    
    # srcディレクトリのPathオブジェクト
    path = pathlib.Path(path_name_from)
    assert path.is_dir()  # 指定されたパスがディレクトリではない時はエラー

    # path以下の全てのファイルとディレクトリ
    files = list(path.glob("**/*"))

    # ディレクトリは除外
    files = list(filter(is_file, files))

    # 更新日時でソート
    files.sort(key=modified_time, reverse=True)

    # 出力する数
    n_export = min(len(files), N_MAX_COPY)
        
    # destディレクトリに同期候補をコピー
    for i in range(n_export):
        subprocess.run(
            "copy" + 
            " \"" + str(files[i]) + "\"" +
            " \"" + path_name_to + "\"", shell=True)
    return


def is_file(path):
    return path.is_file()


def modified_time(path):
    return path.stat().st_mtime


if __name__ == "__main__": main()

あとがき

ローカルにある写真フォルダから,最近撮ったものを抽出してクラウドストレージに同期しておきたかった.写真は,基本的にはローカルのアーカイブにあれば十分なのだけれど,新しいファイルは時々スマホなどから確認したくなる場合がある.