【问题标题】:PyGTK/GIO: monitor directory for changes recursivelyPyGTK/GIO:递归监控目录的变化
【发布时间】:2010-06-18 10:29:59
【问题描述】:

获取以下演示代码(从GIO answer 到这个问题),它使用 GIO FileMonitor 来监视目录的更改:

import gio

def directory_changed(monitor, file1, file2, evt_type):
    print "Changed:", file1, file2, evt_type

gfile = gio.File(".")
monitor = gfile.monitor_directory(gio.FILE_MONITOR_NONE, None)
monitor.connect("changed", directory_changed) 

import glib
ml = glib.MainLoop()
ml.run()

运行此代码后,我可以创建和修改子节点并收到更改通知。但是,这仅适用于直系子女(我知道文档没有另外说明)。以下最后一个 shell 命令不会产生通知:

touch one
mkdir two
touch two/three

有没有简单的方法让它递归?我宁愿不手动编写查找目录创建并添加监视器、删除时删除它们等的代码。

预期用途是用于 VCS 文件浏览器扩展,以便能够缓存工作副本中的文件状态并在更改时单独更新它们。因此,可能有数万到数千(或更多)目录需要监控。我只想找到工作副本的根目录并在那里添加文件监视器。

我知道pyinotify,但我避免使用它,以便它可以在非 Linux 内核(如 FreeBSD 或...其他)下工作。据我所知,GIO FileMonitor 在可用的情况下在下面使用 inotify,我可以理解不强调实现以保持某种程度的抽象,但它向我暗示它应该是可能的。

(以防万一,我最初发布在PyGTK mailing list。)

【问题讨论】:

    标签: python pygtk


    【解决方案1】:

    “有没有简单的方法 递归?”

    我不知道有什么“简单的方法”可以实现这一点。底层系统,例如 Linux 上的 inotify 或 BSD 上的 kqueue 不提供自动添加递归监视的功能。我也不知道有任何库在 GIO 上分层你想要的东西。

    因此,您很可能必须自己构建它。因为这在某些极端情况下可能有点技巧(例如mkdir -p foo/bar/baz),我建议查看 pynotify 如何实现其auto_add 功能(通过pynotify source 进行grep)并将其移植到GIO。

    【讨论】:

      【解决方案2】:

      我不确定 GIO 是否允许您同时拥有多个显示器,但如果允许,您没有*理由不能执行以下操作:

      import gio
      import os
      
      def directory_changed(monitor, file1, file2, evt_type):
          if os.path.isdir(file2):    #maybe this needs to be file1?
              add_monitor(file2) 
          print "Changed:", file1, file2, evt_type
      
      def add_monitor(dir):
          gfile = gio.File(dir)
          monitor = gfile.monitor_directory(gio.FILE_MONITOR_NONE, None)
          monitor.connect("changed", directory_changed) 
      
      add_monitor('.')
      
      import glib
      ml = glib.MainLoop()
      ml.run()
      

      *当我说没有理由时,这有可能成为资源消耗,尽管我对 GIO 的了解几乎为零,但我不能真正说出来。也完全可以使用一些命令(os.listdir 等)在 Python 中创建自己的。它可能看起来像这样

      import time
      import os
      
      class Watcher(object):
          def __init__(self):
              self.dirs = []
              self.snapshots = {}
      
          def add_dir(self, dir):
              self.dirs.append(dir)
      
          def check_for_changes(self, dir):
              snapshot = self.snapshots.get(dir)
              curstate = os.listdir(dir)
              if not snapshot:
                  self.snapshots[dir] = curstate
              else:
                  if not snapshot == curstate:
                      print 'Changes: ',
                      for change in set(curstate).symmetric_difference(set(snapshot)):
                          if os.path.isdir(change):
                              print "isdir"
                              self.add_dir(change)
                          print change,
      
                      self.snapshots[dir] = curstate
                      print
      
          def mainloop(self):
              if len(self.dirs) < 1:
                  print "ERROR: Please add a directory with add_dir()"
                  return
      
              while True:
                  for dir in self.dirs:
                      self.check_for_changes(dir)
                  time.sleep(4) # Don't want to be a resource hog
      
      w = Watcher()
      w.add_dir('.')
      
      
      w.mainloop()
      

      【讨论】:

      • 这给了我一个很好的起点,谢谢 :) 我看不出它不允许我有多台显示器的理由。我只需要对其进行测试以查看它是否过于占用资源,但由于我正在查看目录而不是文件,这可能会节省我的时间。由于我正在监控版本控制树,它还让我可以灵活地忽略管理目录(例如.svn.git)。
      • 我必须检查是否需要手动取消和删除已删除目录的监视器。
      • 您还可以创建一个忽略文件并根据忽略文件中的文件名检查文件名。最简单的方法就是每行一个文件,每次f.seek(0)ignorelist = f.readlines()if change in ignorelist: #skip
      • 请注意,文件系统监视器不使用带睡眠的循环。他们使用 inotify 订阅来自内核的事件。
      猜你喜欢
      • 2010-10-05
      • 2023-03-15
      • 2015-09-19
      • 2011-08-18
      • 1970-01-01
      • 2011-11-23
      • 2016-10-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多