用Python watchdog生成多个观察者

20

我目前有一个基本的脚本,监视单个目录及其所有子目录的变化,并将输出传递给LoggingEventHandler。

现在我想扩展我的脚本以监视3个不同的位置,但我无法理解如何生成多个观察者来监视我指定的每个路径。

我尝试了以下类似的方法:

import time
import thread
import threading
from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler

event_handler = LoggingEventHandler()
observer = Observer()

paths = ["C:\dir1", "C:\dir2", "C:\dir3"]

for i in paths:
    targetPath = str(i)
    observer.schedule(event_handler, targetPath, recursive=True)
    observer.start_new_thread()

不幸的是,我收到了一个错误提示,指出观察者没有 'start_new_thread' 属性。

文档中没有展示超过单个观察者监测目录的例子。我没有处理线程的经验,甚至不确定我是否走在了正确的道路上。

我应该为每个路径创建观察者类的新实例吗?还是有一些方法可以将 Observer 类的单个实例馈送给多个路径?

如果有一个显而易见的答案,请原谅我的无知。我确定现在所有这些都是错误的,但我太累了,无法理解。

附加信息:

感谢@FogleBird,我已经纠正了线程启动问题,但我仍然卡在只有一个实例而不是三个不同观察者监测不同路径的情况。我修改后的代码现在看起来像:

threads = []

for i in paths:
    targetPath = str(i)
    observer.schedule(event_handler, targetPath, recursive=True)
    threads.append(observer)

observer.start()
print threads

这会返回三个ObservedWatch对象,但它们的细节都相同:

[<Observer(Thread-1, started daemon 1548)>, <Observer(Thread-1, started daemon 1548)>, <Observer(Thread-1, started daemon 1548)>]

看起来仍然完全不对,任何更多的帮助都将是极好的。我很难理解这个概念。

附加 2:

我继续摆弄代码,现在我有了一个似乎是可用的东西:

event_handler = LoggingEventHandler()
N2watch = Observer()
threads = []

for i in paths:
    targetPath = str(i)
    N2watch.schedule(event_handler, targetPath, recursive=True)
    threads.append(N2watch)

N2watch.start()

try:
    while True:
            time.sleep(1)
except KeyboardInterrupt:
    N2watch.stop()
N2watch.join()

根据初步运行结果,我认为输出似乎捕捉到了我列表中指定的所有三个路径名称的更改,但我需要编写一些测试代码来进行检查。

我仍然不确定它的表现如何,所以任何进一步的评论都会很好。

谢谢。

附加3:

我将FogleBird的答案标记为最佳答案,因为那是唯一一个,并且确实强调了我初始代码中的问题。

我的上一个编辑包含完全工作的代码,用于监视多个位置,并且目前似乎正常运行。


你解决了这个问题吗?我也在尝试实现同样的功能。 - majidarif
嗨 - 是的,上面帖子中最后一段代码被证明是有效的。标记为 ADDITIONAL 2 的部分以下的所有内容对我都起作用了。 - binarydogs
4个回答

12

很好的问题。这个帖子已经有点老了,但是当我查找确切的内容时,我找到了它,我在你的工作基础上进行了扩展,并添加了传递一个包含要监视的目录列表的文件的功能。默认情况下,我不会递归查找,我留给其他人测试。希望这对任何查找相同主题的人有所帮助。做得好!

使用python watcher.py 文件名来运行。

其中,watcher.py 是我称呼自己的脚本名称,而文件名是包含路径的文件名。

我在文件中列出完整的路径,并用换行符分隔它们。

例如:

C:\path1
C:\Path2\subpath1
C:\PATH3

watcher.py

import logging
import sys
import time
from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler

# Attach a logging event AKA FileSystemEventHandler
event_handler = LoggingEventHandler()

# Create Observer to watch directories
observer = Observer()

# Take in list of paths. If none given, watch CWD
paths = open(sys.argv[1], 'r') if len(sys.argv) > 1 else '.'

# Empty list of observers
observers = []

# Base logging configuration
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S')

# Iterate through paths and attach observers
for line in paths:

    # Convert line into string and strip newline character
    targetPath = str(line).rstrip()

    # Schedules watching of a given path
    observer.schedule(event_handler, targetPath)

    # Add observable to list of observers
    observers.append(observer)

# Start observer
observer.start()

try:
    while True:

        # Poll every second
        time.sleep(1)

except KeyboardInterrupt:
    for o in observers:
        o.unschedule_all()

        # Stop observer if interrupted
        o.stop()

for o in observers:

    # Wait until the thread terminates before exit
    o.join()

6

只想添加一些注释:

对于刚开始使用watchdog(包括我自己)的人来说,代码中的线程库和线程列表可能会有点混淆。它们实际上在解决方案中是不必要的。一个简单的解释是:

  • 创建一个观察者
  • 安排多个“观察事件”
  • 然后启动观察者。

就这样。


这节省了我很多时间!谢谢。 - scaryguy

6
这里的示例代码展示了一个名为start而不是start_new_thread的函数。你试过了吗?
此外,你应该在for循环之后只调用一次start,而不是在其中调用。 https://pypi.python.org/pypi/watchdog

2
谢谢@FogleBird,我不知道在阅读文档时从哪里学到了codestart_new_threadcode - binarydogs

2

这是我用来监视多个目录的代码。

import sys
import time
import logging
from watchdog.observers.polling import PollingObserver as 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')
    event_handler = LoggingEventHandler()
    observer = Observer()
    if len(sys.argv) > 1:
        for i in range(1, len(sys.argv)):
            observer.schedule(event_handler, sys.argv[i], recursive=True)
    else:
        observer.schedule(event_handler, '.', recursive=True)
    observer.start()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()

这个答案是针对同一目录下有多个子目录的情况,因此需要使用recursive=True标志。这与问题所问的不同。 - Vitalis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接