如何在Python中获取文件closures事件

Windows 7 64位机上使用python 2.7。

如何获得文件closures事件:

  1. 当文件在文件打开程序的新进程中打开时(如记事本,每次在新的写字板过程中打开文件的记事本)
  2. 当文件打开在文件打开程序的选项卡(如记事本++,它打开新标签中的所有文件,但只有一个记事本++运行的单一进程)

那么,如何在上述情况下获得文件closures事件呢? 是否有可能通过一个共同的代码来实现上述情况? 我正在处理不同的文件types

这对于nix系统来说已经被证明是非常简单的任务,但是在Windows上,获取文件关闭事件并不是一件简单的事情。 请阅读下面按操作系统分组的常用方法的总结。

对于Linux

在Linux上,可以很容易地监视文件系统的变化,并且非常详细。 最好的工具是称为inotify的内核特性,并且有一个使用它的Python实现,称为Pynotify。

  • Pynotify

    Pyinotify是一个用于监视文件系统更改的Python模块。 Pyinotify依靠一个名为inotify的Linux内核功能(在内核2.6.13中合并),这是一个事件驱动的通知程序。 它的通知通过三个系统调用从内核空间导出到用户空间。 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要复杂得多。 大多数库依赖于ReadDirectoryChanges API,它是受限制的,不能检测到文件关闭事件等更精细的细节。 然而,还有其他的方法来检测这样的事件,所以请继续阅读以了解更多。

  • 守望者

    注意: Watcher已于2011年2月上次更新,所以可能会安全地跳过这个。

    Watcher是一个低级C扩展,用于在Windows系统上使用ReadDirectoryChangesW API接收文件系统更新。 该软件包还包含一个模拟大部分.NET FileSystemWatcher API的高级界面。
    与Watcher最近的一个可以检测文件关闭事件是监视FILE_NOTIFY_CHANGE_LAST_WRITE和/或FILE_NOTIFY_CHANGE_LAST_ACCESS事件。

    用法示例:

     import watcher w = watcher.Watcher(dir, callback) w.flags = watcher.FILE_NOTIFY_CHANGE_LAST_WRITE w.start() 
  • 看家狗

    Python API和shell实用程序来监视文件系统事件。 安装简单: $ pip install watchdog 。 欲了解更多信息,请访问文档 。
    Windows上的看门狗依赖于ReadDirectoryChangesW API,这就像Watcher和其他依赖于相同API的库一样引起了警告。

  • Pywatch

    Linux watch命令的Python近似克隆。 pywatch.watcher.Watcher类可以被告知要观察一组文件,并给予一组命令来运行,只要这些文件中的任何一个改变。 它只能监视文件更改的事件,因为它依靠轮询stat的st_mtime 。

与NTFS的Windows奖金:

  • NTFS USN杂志

    NTFS USN(更新序列号)日志是NTFS的一项功能,它维护对卷进行更改的记录。 它被列为奖金的原因是因为不像其他条目,它不是一个特定的库,而是一个存在于NTFS系统上的功能。 所以,如果你使用其他Windows文件系统(如FAT,ReFS等),这不适用。
    它的工作方式是系统记录对USN日志文件中的卷进行的所有更改,每个卷都有自己的实例。 “更改日志”中的每个记录都包含USN,文件的名称以及有关更改内容的信息。

    这个方法对于这个问题很有意思的主要原因是,与其他方法不同,这个方法提供了一种检测文件关闭事件的方法,定义为USN_REASON_CLOSE 。 有关完整的事件列表的更多信息可以在MSDN文章中找到。 有关USN日记的完整文档,请访问此MSDN页面 。

    从Python访问USN日志有多种方式,但唯一成熟的选择似乎是ntfsjournal模块。

Windows的“正确”方式:

  • 文件系统过滤器驱动

    正如在MSDN页面上所描述的那样:

    文件系统筛选器驱动程序是一个可选的驱动程序,它可以增加或修改文件系统的行为。 文件系统过滤器驱动程序是作为Windows执行程序的一部分运行的内核模式组件。 文件系统筛选器驱动程序可以筛选一个或多个文件系统或文件系统卷的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_() 

你面临的问题不是Python,而是Windows。 它可以完成,但是你将不得不为它编写一些非平凡的C / C ++代码。

Windows上的userland中不存在文件打开或文件关闭用户模式通知。 这就是为什么其他人建议的图书馆没有文件关闭通知。 在Windows中,用于检测用户区更改的API是ReadDirectoryChangesW 。 它会提醒您以下通知之一 :

  • FILE_ACTION_ADDED如果文件被添加到目录。
  • FILE_ACTION_REMOVED是否从目录中删除文件。
  • FILE_ACTION_MODIFIED如果文件被修改。 这可能是时间戳或属性的变化。
  • FILE_ACTION_RENAMED_OLD_NAME如果文件被重命名,这是旧名称。
  • FILE_ACTION_RENAMED_NEW_NAME如果文件被重命名,并且这是新名称。

没有多少Python可以改变Windows提供给你的东西。

要获得文件关闭通知, 像Process Monitor这样的工具会安装一个Minifilter,它驻留在内核中 ,靠近EFS等其他过滤器的顶部。

为了达到你想要的,你需要:

  1. 安装具有代码发送事件回到userland的小型过滤器。 使用微软的Minispy示例 ,它是稳定和快速的。
  2. 转换user程序中的代码,使其成为一个Python扩展( minispy.pyd ),公开一个生成事件的生成器。 这是困难的部分,我会回到这一点。
  3. 你将不得不过滤事件,你不会相信在一个空闲的Windows盒上IO的数量!
  4. 你的Python程序可以导入你的扩展并完成它的工作。

整个事情看起来像这样:

用于文件系统事件的Windows Minifilter上的Python包装器

当然你可以通过NTFS使用EFS,这只是为了表明你的微型过滤器将超越所有的。

困难的部分:

  • 您的微型过滤器必须由微软信任的机构进行数字签名。 Verising出现在脑海中,但也有其他的。
  • 调试需要一个单独的(虚拟)机器,但是你可以使你的界面容易模拟。
  • 您将需要安装具有管理员权限的帐户的微型过滤器。 任何用户将能够阅读事件。
  • 你将不得不面对你自己的多用户。 许多用户只有一个微过滤器。
  • 您必须将用户程序从MiniSpy示例转换为一个DLL,您将使用Python扩展名进行包装。

最后两个是最难的。

你可以使用Pyfanotyfi或黄油 。

我想你会发现这个链接非常有用: 与C,Python和Ruby的Linux文件系统事件

在那里你会找到一个关于做你想要的东西(使用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() 

我还没有找到一个在Windows上捕获openclose事件的软件包。 正如其他人所说, pyinotify ,对于基于Linux的操作系统来说是一个很好的选择。

由于我无法观看封闭的事件,我决定修改事件。 这是一个“事后”类型的解决方案(即,我不能暂停,直到我看到一个文件被关闭)。 但是,这个工作出人意料的好。

我使用了看门狗软件包。 下面的代码来自它们的示例实现,并且如果不在命令行中传递路径,则会观察当前目录,否则它将监视传递的路径。

示例调用: python test.pypython 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 事件来进行过滤。