Windowsでフォルダ内容の監視
タイトルの通り、フォルダ内容の変化を監視するスクリプトです。
検索したら、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(".")