タイトルの通り、フォルダ内容の変化を監視するスクリプトです。
検索したら、http://tgolden.sc.sabren.com/python/win32_how_do_i/watch_directory_for_changes.htmlのサイトに全て書かれていました。
以下の内容は、彼のサイトのデッドコピーです。
win32上のPythonでフォルダを監視するには、
- os.listdirを使う
- FindFirstChangeNotificationを使う
- ReadDirectoryChangesを使う
3つの方法があります。
使いやすさと効率は、FindFirstChangeNotificationを使う方法が一番バランスが取れていると思います。
os.listdirを使う
メリット
- シンプルで書くのも理解するのも簡単
- 標準モジュールしか使わない→クロスプラットフォーム
デメリット
- いちいちos.listdirでアクセスするので、ファイル数が増えると遅い。
- ファイルの「更新」を検出できない(os.statなどを使う必要がある)。
from __future__ import division, print_function import os import time def watch0(path_to_watch): before = set(os.listdir(path_to_watch)) while 1: time.sleep(10) after = set(os.listdir(path_to_watch)) added = after - before removed = before - after if added: print("Added:", ", ".join(added)) if removed: print("Removed:", ", ".join(removed)) before = after watch0(".")
FindFirstChangeNotificationを使う
メリット
- 実際にフォルダに変化があった時だけアクセスするので、負荷が少ない
- 「名前の変更は通知する」「サイズ変更は無視する」など、通知の内容を細かく設定できる
デメリット
- 頻度は少ないとはいえ、os.listdirを使う必要がある
from __future__ import division, print_function import os import win32file import win32event import win32con def watch1(path_to_watch="."): change_handle = win32file.FindFirstChangeNotification( path_to_watch, 0, win32con.FILE_NOTIFY_CHANGE_FILE_NAME, ) try: old_path_contents = set(os.listdir(path_to_watch)) while 1: result = win32event.WaitForSingleObject(change_handle, 500) if result == win32con.WAIT_OBJECT_0: new_path_contents = set(os.listdir(path_to_watch)) added = new_path_contents - old_path_contents deleted = old_path_contents - new_path_contents if added: print("Added:", ", ".join(added)) if deleted: print("Deleted:", ", ".join(deleted)) old_path_contents = new_path_contents win32file.FindNextChangeNotification (change_handle) finally: win32file.FindCloseChangeNotification (change_handle) watch1()
ReadDirectoryChangesを使う
メリット
- 実際にフォルダに変化があった時だけアクセスするので、負荷が少ない
- 通知の内容を細かく設定できる
- どのファイルがどう変化したかを検出できる
- os.listdirを使う必要が無い
デメリット
- フォルダに変化が無い時にループを抜けるのは簡単ではありません。
from __future__ import division, print_function def watch2(path_to_watch): FILE_LIST_DIRECTORY = 0x0001 hDir = win32file.CreateFile( path_to_watch, FILE_LIST_DIRECTORY, win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE, None, win32con.OPEN_EXISTING, win32con.FILE_FLAG_BACKUP_SEMANTICS, None, ) while 1: filt = (win32con.FILE_NOTIFY_CHANGE_FILE_NAME | win32con.FILE_NOTIFY_CHANGE_DIR_NAME | win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES | win32con.FILE_NOTIFY_CHANGE_SIZE | win32con.FILE_NOTIFY_CHANGE_LAST_WRITE | win32con.FILE_NOTIFY_CHANGE_SECURITY) results = win32file.ReadDirectoryChangesW( hDir, 1024, True, filt, None, None ) ACTIONS = { 1: "Created", 2: "Deleted", 3: "Updated", 4: "Renamed from something", 5: "Renamed to something" } for action, file in results: full_filename = os.path.join(path_to_watch, file) print(full_filename, ACTIONS.get(action, "Unknown")) watch2(".")