11

在 Windows 7 64 位机器上使用 python 2.7。

如何获取文件关闭事件:

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

那么,如何在上述情况下获取文件关闭事件?是否可以通过通用代码实现上述情况?我正在处理不同的文件类型

4

4 回答 4

19

对于 *nix 系统,这已被证明是一项非常容易的任务,但在 Windows 上,获取文件关闭事件并不是一项简单的任务。请阅读下面按操作系统分组的常用方法的摘要。

对于 Linux

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

  • Pyinotify

    Pyinotify是一个用于监视文件系统更改的 Python 模块。Pinotify 依赖于一个名为 的 Linux 内核特性(在内核 2.6.13 中合并)inotify,它是一个事件驱动的通知程序。它的通知通过三个系统调用从内核空间导出到用户空间。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是用于在 Windows 系统上使用 APIC接收文件系统更新的低级扩展。ReadDirectoryChangesW该软件包还包括一个高级接口,用于模拟大多数 .NET FileSystemWatcherAPI。
    最接近使用 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 上的 Watchdog 依赖于ReadDirectoryChangesWAPI,这带来了与 Watcher 和其他依赖于相同 API 的库一样的警告。

  • Pywatch

    watchLinux命令的 python 近似克隆。pywatch.watcher.Watcher可以告诉该类监视一组文件,并给出一组命令以在任何这些文件发生更改时运行。它只能监视文件更改事件,因为它依赖于轮询统计信息的 st_mtime

带有 NTFS 的 Windows 的奖励:

  • NTFS USN 期刊

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

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

    从 Python 访问 USN Journal 有多种方法,但唯一成熟的选项似乎是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_()
    
于 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如果文件被重命名并且这是新名称。

再多的 Python 也无法改变 Windows 为您提供的功能。

为了获得文件关闭通知,Process Monitor 之类的工具安装了一个Minifilter,它位于内核中,靠近 EFS 等其他过滤器的顶部。

为了实现你想要的,你需要:

  1. 安装一个包含将事件发送回用户空间的代码的 Minifilter。使用微软的Minispy sample,稳定快速。
  2. 转换user程序中的代码,使其成为 Python 扩展 ( minispy.pyd),公开生成事件的生成器。这是困难的部分,我会回到那个。
  3. 您将不得不过滤掉事件,您不会相信空闲 Windows 机器上的 IO 量!
  4. 然后,您的 Python 程序可以导入您的扩展并执行其操作。

整个事情看起来像这样:

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

当然,您可以在 NTFS 上使用 EFS,这只是为了表明您的微过滤器将高于一切。

困难的部分:

  • 您的微过滤器必须由 Microsoft 信任的权威机构进行数字签名。想到了验证,但还有其他的。
  • 调试需要一台单独的(虚拟)机器,但您可以使您的界面易于模拟。
  • 您将需要使用具有管理员权限的帐户安装微过滤器。任何用户都可以阅读事件。
  • 您将不得不自己处理多用户。许多用户只有一个微过滤器。
  • 您必须将用户程序从 MiniSpy 示例转换为 DLL,您将使用 Python 扩展对其进行包装。

最后两个是最难的。

于 2014-03-25T21:19:29.810 回答
0

我还没有找到在 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 事件来过滤刚刚修改的内容。

于 2014-03-24T19:34:22.857 回答
0

您可以使用Pyfanotyfi黄油

我想你会发现这个链接非常有用: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 回答