11

Windows 7 64ビットマシンでpython 2.7を使用しています。

ファイルクローズイベントを取得する方法:

  1. ファイルオープナーの新しいプロセスでファイルが開かれたとき(メモ帳、ワードパッドの新しいプロセスで毎回ファイルを開くワードパッドなど)
  2. ファイルがファイル オープナーのタブで開かれたとき (notepad++ のように、すべてのファイルを新しいタブで開きますが、実行中の notepad++ のプロセスは 1 つしかありません)

では、上記の場合にファイルクローズイベントを取得するにはどうすればよいですか? 共通のコードで上記のケースを達成することは可能ですか? さまざまなファイルタイプを扱っています

4

4 に答える 4

19

これは、*nix システムでは非常に簡単なタスクであることが証明されていますが、Windows では、ファイル クローズ イベントを取得するのは簡単なタスクではありません。OS ごとにグループ化された一般的な方法の概要を以下でお読みください。

Linux の場合

Linux では、ファイルシステムの変更を簡単に、しかも詳細に監視できます。これに最適なツールは と呼ばれるカーネル機能inotifyであり、それを使用する Pynotify と呼ばれる Python 実装があります。

  • Pyinotify

    Pyinotifyファイルシステムの変更を監視するための Python モジュールです。Pyinotifyinotifyは、イベント駆動型通知機能である と呼ばれる Linux カーネル機能 (カーネル 2.6.13 に統合) に依存しています。その通知は、3 つのシステム コールを介してカーネル空間からユーザー空間にエクスポートされます。Pyinotifyこれらのシステム コールをバインドし、それらの機能を操作するための一般的で抽象的な方法を提供するそれらの上に実装を提供します。

    ここでは、 で監視できるイベントのリストを見つけることができますPynotify

    使用例:

    pyinotify をインポート

    class EventHandler(pyinotify.ProcessEvent):
        def process_IN_CLOSE_NOWRITE(self, event):
            print "File was closed without writing: " + event.pathname
        def process_IN_CLOSE_WRITE(self, event):
            print "File was closed with writing: " + event.pathname
    
    def watch(filename):
        wm = pyinotify.WatchManager()
        mask = pyinotify.IN_CLOSE_NOWRITE | pyinotify.IN_CLOSE_WRITE
        wm.add_watch(filename, mask)
    
        eh = EventHandler()
        notifier = pyinotify.Notifier(wm, eh)
        notifier.loop()
    
    if __name__ == '__main__':
        watch('/path/to/file')
    

Windows の場合

Windows の状況は、Linux の場合よりもかなり複雑です。ほとんどのライブラリは、制限された API に依存しておりReadDirectoryChanges、ファイル クローズ イベントなどの詳細を検出できません。ただし、このようなイベントを検出する方法は他にもあります。詳細については、以下をお読みください。

  • ウォッチャー

    注: Watcher は 2011 年 2 月に最後に更新されたため、おそらくこれをスキップしても安全です。

    WatcherWindows システムで API をC使用してファイル システムの更新を受信するための低レベルの拡張機能です。このパッケージには、ほとんどの .NET APIReadDirectoryChangesWをエミュレートするための高レベル インターフェイスも含まれています。 Watcher を使用してファイル クローズ イベントを検出する最も近い方法は、and/orイベントを監視することです。 FileSystemWatcher
    FILE_NOTIFY_CHANGE_LAST_WRITEFILE_NOTIFY_CHANGE_LAST_ACCESS

    使用例:

    import watcher
    w = watcher.Watcher(dir, callback)
    w.flags = watcher.FILE_NOTIFY_CHANGE_LAST_WRITE
    w.start()
    
  • ウォッチドッグ

    ファイル システム イベントを監視するための Python API およびシェル ユーティリティ。簡単なインストール: $ pip install watchdog. 詳細については、ドキュメントを参照してください。
    Windows 上の Watchdog はReadDirectoryChangesWAPI に依存しているため、Watcher や同じ API に依存する他のライブラリと同様に注意が必要です。

  • パイウォッチ

    watchLinuxコマンドの python に近いクローン。クラスは一連のpywatch.watcher.Watcherファイルを監視するように指示され、それらのファイルのいずれかが変更されるたびに実行する一連のコマンドが与えられます。統計の st_mtime のポーリングに依存しているため、ファイル変更イベントのみを監視できます。

NTFS を使用する Windows のボーナス:

  • NTFS USN ジャーナル

    NTFS USN (更新シーケンス番号) ジャーナルは、ボリュームに加えられた変更の記録を保持する NTFS の機能です。ボーナスとしてリストされている理由は、他のエントリとは異なり、特定のライブラリではなく、NTFS システムに存在する機能であるためです。したがって、他の Windows ファイルシステム (FAT、ReFS など) を使用している場合、これは当てはまりません。
    システムは、ボリュームに対して行われたすべての変更を USN ジャーナル ファイルに記録し、各ボリュームは独自のインスタンスを持ちます。変更ジャーナルの各レコードには、USN、ファイル名、変更内容に関する情報が含まれています。

    このメソッドがこの質問にとって興味深い主な理由は、他のほとんどのメソッドとは異なり、このメソッドがUSN_REASON_CLOSEとして定義されたファイル クローズイベントを検出する方法を提供することです。イベントの完全なリストを含む詳細については、このMSDN 記事 を参照してください。USN ジャーナリングに関する完全なドキュメントについては、このMSDN ページにアクセスしてください。

    Python から USN Journal にアクセスする方法は複数ありますが、成熟した唯一のオプションはntfsjournalモジュールのようです。

Windows の「適切な」方法:

  • ファイル システム フィルター ドライバー

    MSDNページで説明されているように:

    ファイル システム フィルター ドライバーは、ファイル システムに価値を追加したり、ファイル システムの動作を変更したりするオプションのドライバーです。ファイル システム フィルター ドライバーは、Windows エグゼクティブの一部として実行されるカーネル モード コンポーネントです。ファイル システム フィルター ドライバーは、1 つ以上のファイル システムまたはファイル システム ボリュームの I/O 操作をフィルター処理できます。ドライバーの性質に応じて、フィルターは、ログ、監視、変更、さらには防止を意味する場合があります。ファイル システム フィルター ドライバーの一般的なアプリケーションには、ウイルス対策ユーティリティ、暗号化プログラム、階層型ストレージ管理システムなどがあります。

    ファイル システム フィルター ドライバーを実装するのは簡単な作業ではありませんが、試してみたい人のために、CodeProjectに優れた入門チュートリアルがあります。

    PS この方法に関する追加情報については、@ ixe013の回答を確認してください。

マルチプラットフォーム

  • Qt の QFileSystemWatcher

    このQFileSystemWatcherクラスは、ファイルとディレクトリの変更を監視するためのインターフェイスを提供します。このクラスは で導入されましたQt 4.2
    残念ながら、ファイルが変更、名前変更、または削除されたとき、および新しいファイルがディレクトリに追加されたときしか検出できないため、その機能はかなり制限されています。

    使用例:

    import sys
    from PyQt4 import QtCore
    
    def directory_changed(path):
        print('Directory Changed: %s' % path)
    
    def file_changed(path):
        print('File Changed: %s' % path)
    
    app = QtCore.QCoreApplication(sys.argv)
    
    paths = ['/path/to/file']
    fs_watcher = QtCore.QFileSystemWatcher(paths)
    fs_watcher.directoryChanged.connect(directory_changed)
    fs_watcher.fileChanged.connect(file_changed)
    
    app.exec_()
    
于 2014-03-14T13:43:10.927 に答える
3

あなたが直面している問題は Python ではなく、Windows にあります。それは可能ですが、そのためにいくつかの非自明な C/C++ コードを作成する必要があります。

ファイル オープンまたはファイル クローズのユーザー モード通知は、Windows のユーザーランドには存在しません。そのため、他の人が提案したライブラリにはファイル クローズ通知がありません。Windows では、ユーザーランドの変更を検出する API はReadDirectoryChangesWです。次のいずれかの通知が表示されます。

  • FILE_ACTION_ADDEDファイルがディレクトリに追加された場合。
  • FILE_ACTION_REMOVEDファイルがディレクトリから削除された場合。
  • FILE_ACTION_MODIFIEDファイルが変更された場合。これは、タイムスタンプまたは属性の変更である可能性があります。
  • FILE_ACTION_RENAMED_OLD_NAMEファイルの名前が変更され、これが古い名前である場合。
  • FILE_ACTION_RENAMED_NEW_NAMEファイルの名前が変更され、これが新しい名前である場合。

Windows が提供するものを Python で変更することはできません。

ファイル クローズ通知を取得するために、Process Monitor などのツールは、EFS などの他のフィルターの上部近くに、カーネル内に存在するミニフィルターをインストールします。

あなたが望むものを達成するには、次のことが必要です。

  1. イベントをユーザーランドに送り返すコードを含む Minifilter をインストールします。Microsoft の Minispy サンプルを使用してください。安定して高速です。
  2. プログラムのコードを変換して、イベントを生成するジェネレーターを公開するuserPython 拡張機能 ( ) にします。minispy.pydこれは難しい部分です。私はそれに戻ります。
  3. イベントを除外する必要があります。アイドル状態の Windows ボックスでの IO の量を信じることはできません。
  4. その後、Python プログラムは拡張機能をインポートして、その機能を実行できます。

全体は次のようになります。

ファイルシステム イベント用の Windows ミニフィルターに対する Python ラッパー

もちろん、NTFS で EFS を使用することもできます。これは、ミニフィルターがそれよりも優れていることを示すためのものです。

難しい部分:

  • ミニフィルターは、Microsoft が信頼する機関によってデジタル署名されている必要があります。Verising が思い浮かびますが、他にもあります。
  • デバッグには別の (仮想) マシンが必要ですが、インターフェイスを簡単にモックできます。
  • 管理者権限を持つアカウントでミニフィルターをインストールする必要があります。すべてのユーザーがイベントを読み取ることができます。
  • 自分でマルチユーザーに対処する必要があります。多くのユーザーに対して、ミニフィルターは 1 つしかありません。
  • ユーザー プログラムを MiniSpy サンプルから DLL に変換する必要があります。DLL は Python 拡張機能でラップします。

最後の2つは最も難しいです。

于 2014-03-25T21:19:29.810 に答える
0

openWindows でキャプチャおよびcloseイベントを実行するパッケージが見つかりません。他の人が述べたように、pyinotifyは、Linux ベースのオペレーティング システムに最適なオプションです。

クローズドイベントが見れなかったので、修正イベントで我慢しました。これは非常に「事後」タイプのソリューションです (つまり、ファイルが閉じられるまで一時停止することはできません)。でも、これが意外とうまくいきました。

watchdogパッケージを使用しました。以下のコードはサンプル実装からのもので、コマンド ラインでパスを渡さない場合は現在のディレクトリを監視します。それ以外の場合は、渡すパスを監視します。

呼び出しの例: python test.pyまたはpython test.py C:\Users\Administrator\Desktop

import sys
import time
import logging
from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler
if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s - %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S')
    path = sys.argv[1] if len(sys.argv) > 1 else '.'
    event_handler = LoggingEventHandler()
    observer = Observer()
    observer.schedule(event_handler, path, recursive=True)
    observer.start()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join() 

このコードは、ファイルが作成、変更、削除、または名前変更/移動されたときに表示されます。on_modified イベントを監視することで、変更しただけでフィルタリングできます。

于 2014-03-24T19:34:22.857 に答える
0

Pyfanotyfiまたはbutterを使用できます。

このリンクは非常に役立つと思います: Linux file system events with C, Python and Ruby

そこでは、( pyinotifyを使用して)あなたが望むことを正確に行う例が見つかります。これはコードです:

import pyinotify

DIR_TO_WATCH="/tmp/notify-dir"
FILE_TO_WATCH="/tmp/notify-dir/notify-file.txt"

wm = pyinotify.WatchManager()

dir_events = pyinotify.IN_DELETE | pyinotify.IN_CREATE
file_events = pyinotify.IN_OPEN | pyinotify.IN_CLOSE_WRITE | pyinotify.IN_CLOSE_NOWRITE

class EventHandler(pyinotify.ProcessEvent):
    def process_IN_DELETE(self, event):
        print("File %s was deleted" % event.pathname) #python 3 style print function
    def process_IN_CREATE(self, event):
        print("File %s was created" % event.pathname)
    def process_IN_OPEN(self, event):
        print("File %s was opened" % event.pathname)
    def process_IN_CLOSE_WRITE(self, event):
        print("File %s was closed after writing" % event.pathname)
    def process_IN_CLOSE_NOWRITE(self, event):
        print("File %s was closed after reading" % event.pathname)

event_handler = EventHandler()
notifier = pyinotify.Notifier(wm, event_handler)

wm.add_watch(DIR_TO_WATCH, dir_events)
wm.add_watch(FILE_TO_WATCH, file_events)

notifier.loop()
于 2014-03-14T13:45:45.520 に答える