Python之文件监控(watchdog和pyinotify)

在python中文件监控主要有两个库:pyinotify依赖于Linux平台的inotify,Linux平台推荐使用此模块,功能比watchdog强大且全面、watchdog。

watchdog模块

主要有3个角色:observer、event_handler、被监控的文件夹。三者原本是独立的,主要通过observer.schedule函数将三者串起来,observer不断检测调用平台依赖代码对监控文件夹进行变动检测,当发现改变时,通知event_handler处理。

watchdog的3个角色

  1. Observer
    主要作用是当作启动和关闭监控程序,给予文件夹路径后,Observer会监控文件夹的变化并且反馈变化。
    使用from watchdog.observers import Observer命令导入。
  2. events
    watchdog的动作模块,具体作用是根据Observer模块反馈的事件分配不一样的操作动作方法,然后程序再继承这个方法开始执行所需的代码。
    使用from watchdog.events import *命令导入。
  3. 监控文件夹路径

代码实例

# 本模块的功能:<检测文件或目录变化>

# 导入watchdog对应模块
from watchdog.observers import Observer
from watchdog.events import *
# 导入时间模块
import time

class FileEventHandler(FileSystemEventHandler):
    # 初始化
    def __init__(self):
        FileSystemEventHandler.__init__(self)

    # 移动目录或文件
    def on_moved(self, event):
        now=time.strftime("%Y-%m-%d %H:%M;%S",time.localtime())
        if event.is_directory:
            print(f"{now} 移动目录从{event.src_path}到{event.dest_path}")
        else:
            print(f"{now} 移动文件从{event.src_path}到{event.dest_path}")

    # 创建目录或文件
    def on_created(self, event):
        now=time.strftime("%Y-%m-%d %H:%M;%S",time.localtime())
        if event.is_directory:
            print(f"{now} 创建目录{event.src_path}")
        else:
            print(f"{now} 创建文件{event.src_path}")

    # 删除目录或文件
    def on_deleted(self, event):
        now=time.strftime("%Y-%m-%d %H:%M;%S",time.localtime())
        if event.is_directory:
            print("{now} 删除目录{0}".format(event.src_path))
        else:
            print("{now} 删除文件{0}".format(event.src_path))

    # 修改目录或文件
    def on_modified(self, event):
        now=time.strftime("%Y-%m-%d %H:%M;%S",time.localtime())
        if event.is_directory:
            print("{now} 修改目录{0}".format(event.src_path))
        else:
            print("{now} 修改文件{0}".format(event.src_path))

if __name__ == "__main__":
    # 实例化Observer对象
    observer = Observer()
    event_handler = FileEventHandler()
    # 设置监听目录
    dis_dir = "F:\迅雷下载"
    observer.schedule(event_handler,dis_dir,recursive=True) # recursive=True,可以直接写True,表示递归子目录
    print(f"监控目录 {dis_dir}")
    observer.start()
    # try:
    #     while True:
    #         # 设置监听频率(间隔周期时间)
    #         time.sleep(1)
    # except KeyboardInterrupt:
    #     observer.stop()
    observer.join()

pyinotify模块

依赖于Linux内核的inotify功能,inotify是一个事件驱动的通知器,其通知接口从内核空间到用户空间通过三个系统调用。
Notifier是pyinotify模块最重要的类,用来读取通知和处理事件,默认情况下,Notifier处理事件的方式是打印事件。
Notifier类在初始化时接受多个参数,但是只有WatchManager对象是必须传递的参数,WatchManager对象保存了需要监视的文件和目录,以及监视文件和目录的哪些事件,Notifier类根据WatchManager对象中的配置来决定如何处理事件。

#!/usr/bin/env python3
 
import pyinotify

# 创建WatchManager对象 
wm = pyinotify.WatchManager()
# 添加要监控的目录,以及要监控的事件,这里ALL_EVENT表示所有事件         
wm.add_watch('/tmp',pyinotify.ALL_EVENTS)  
# 交给Notifier进行处理
notifier = pyinotify.Notifier(wm)
# 循环处理事件      
notifier.loop()                            

事件标志

下面列举的是事件标志位,可以用'与'来关联监控多个事件,例如multi_event = pyinotify.IN_OPEN | pyinotify.IN_CLOSE_NOWRITE

  1. IN_ACCESS
    被监控项目或者被监控目录中的文件被访问,比如一个文件被读取
  2. IN_MODIFY
    被监控项目或者被监控目录中的文件被修改
  3. IN_ATTRIB
    被监控项目或者被监控目录中的文件的属性被修改
  4. IN_CLOSE_WRITE
    一个可写的文件或者目录被关闭
  5. IN_CLOSE_NOWRITE
    一个只读的文件或者目录被关闭
  6. IN_OPEN
    文件或者目录被打开
  7. IN_MOVED_FROM
    被监控项目或者目录中的文件被移除监控区域
  8. IN_MOVED_TO
    文件或目录被移入监控区域
  9. IN_MOVE_SELF
    自移动,即一个可执行文件在执行时移动自己
  10. IN_CREATE
    在所监控的目录中创建子目录或文件
  11. IN_DELETE
    在所监控的目录中删除目录或文件
  12. IN_DELETE_SELF
    自删除,即一个可执行文件在执行时删除自己
  13. IN_CLOSE
    文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
  14. IN_MOVE
    文件被移动,等同于(IN_MOVED_FROM | IN_MOVED_TO)
  15. IN_UNMOUNT
    宿主文件系统被umount

事件处理

前面的例子,我们使用的都是默认的事件处理方式:打印在屏幕上,而很多情况下我们需要定制事件的处理方式,来实现特殊的功能。定制事件需要继承ProcessEvent类,针对需要处理的事件编写process_事件名(self,event)函数即可

import pyinotify
 
 # 监控多个事件:文件被打开或者只读文件被关闭
multi_event = pyinotify.IN_OPEN | pyinotify.IN_CLOSE_NOWRITE
# 创建WatchManager对象
wm = pyinotify.WatchManager()                                   
 
 # 定制化事件处理类,注意继承
class MyEventHandler(pyinotify.ProcessEvent): 
    # 必须为process_事件名称,event表示事件对象                  
    def process_IN_OPEN(self,event): 
        # event.pathname 表示触发事件的文件路径                         
        print('OPEN',event.pathname)                            
    def process_IN_CLOSE_NOWRITE(self,event):
        print('CLOSE_NOWRITE',event.pathname)
# 实例化我们定制化后的事件处理类
handler = MyEventHandler() 
# 在notifier实例化时传入,notifier会自动执行                                     
notifier = pyinotify.Notifier(wm,handler)                       
# 添加监控的目录及事件
wm.add_watch('/tmp',multi_event)                                
notifier.loop()