Python使用watchfiles实现监控目录变更

 更新时间:2023年09月15日 09:30:26   作者:古明地觉的编程教室  
在工作中难免会碰到这样的需求,监控指定目录,下面小编就来和大家介绍一下如何利用watchfiles 模块实现监控目录的变更,感兴趣的可以了解下

在工作中难免会碰到这样的需求,监控指定目录,如果该目录下发生文件变更,那么进行一系列的处理。而如何监视一个目录,就是我们本次探讨的主题。

监视目录我们可以使用 watchfiles 模块,该模块不仅简单,而且性能也不错。主要原因是,和底层文件系统交互的代码是基于 Rust 编写的,所以性能是有保证的。

通过 pip install watchfiles 安装之后,我们来看看它的用法。

from watchfiles import watch
# 当前目录为 /Users/satori/Desktop/project
for change in watch("."):
    print(change)

我们执行此程序,会处于阻塞状态,并持续监听指定目录的变化。然后我们在当前目录创建几个文件,看看效果。

创建一个 data.txt 文本文件,程序输出如下:

{(<Change.added: 1>, '/Users/satori/Desktop/project/data.txt')}

返回的是一个集合,目前只涉及一个文件的变更,所以集合里面只有一个元素。而集合里面存储的都是元组,元组的第一个元素表示操作类型,总共有三种:分别是增加、修改和删除。

元组的第二个参数就是具体的文件路径,因此程序的输出就告诉我们,当前目录新增了一个 data.txt。

再创建一个 txt_files 目录,程序输出如下:

{(<Change.added: 1>, '/Users/satori/Desktop/project/txt_files')}

不管是目录文件还是文本文件,都属于文件,所以输出是一样的。如果想知道新增的到底是目录还是普通文件,那么还需要通过 os 模块检测一下。

我们在 txt_files 目录中创建一个 data.txt,程序输出如下:

{(<Change.added: 1>, '/Users/satori/Desktop/project/txt_files/data.txt')}

所以 watch 函数监听的不仅是指定目录,其内部的递归子目录也会一并监听。

问题来了,当前目录下存在一个 data.txt 文件和一个 txt_files 目录,而 txt_files 目录也存在一个 data.txt。那么如果将当前目录的 data.txt 移动到 txt_files 中,并同意覆盖,那么程序会输出什么呢?

{(<Change.deleted: 3>, '/Users/satori/Desktop/project/txt_files/data.txt'), 
 (<Change.added: 1>, '/Users/satori/Desktop/project/txt_files/data.txt'), 
 (<Change.deleted: 3>, '/Users/satori/Desktop/project/data.txt')}

此时输出的集合包含三个元组,因此该过程涉及到三次文件的变更。因为 txt_files 里面的文件被替换掉了,所以相当于先被删除、然后重新创建。而当前目录中的 data.txt 被移走了,因此相当于被删除了。

然后我们再通过 mkdir -p a/b/c 同时创建多级目录,程序输出如下:

{(<Change.added: 1>, '/Users/satori/Desktop/project/a/b/c'), 
 (<Change.added: 1>, '/Users/satori/Desktop/project/a/b'), 
 (<Change.added: 1>, '/Users/satori/Desktop/project/a')}

整个过程还是比较简单的,然后除了 watch 函数之外,还有一个 awatch。这两者的作用是一样的,参数也全部一样,只不过 awatch 需要和协程搭配,我们举个例子。

import sys
import asyncio
from asyncio import StreamReader
from watchfiles import awatch, Change
# 监视指定目录
async def watch_files(path):
    # awatch(...) 返回的是异步生成器,需要通过 async for 遍历
    async for change in awatch(path):
        print("-" * 20)
        # change 是一个集合,里面可能会涉及到多个文件的变更
        for item in change:
            if item[0] == Change.added:
                operation = "你增加了"
            elif item[0] == Change.modified:
                operation = "你修改了"
            else:
                operation = "你删除了"
            print(f"{operation} `{item[1]}`")
        print("\n")
# 读取命令行输入,但是注意:不可以使用 input 函数,因为它是同步阻塞调用
# 这种调用在协程当中是大忌,会阻塞整个线程,我们需要改造成异步模式
async def read_from_stdin():
    reader = asyncio.StreamReader()
    protocol = asyncio.StreamReaderProtocol(reader)
    loop = asyncio.get_running_loop()
    await loop.connect_read_pipe(lambda: protocol, sys.stdin)
    return reader
# read_from_stdin 函数的具体细节暂时不用太关注
# 只需要知道它能异步读取命令行即可,关于这方面的内容后续会介绍
# 然后定义主协程
async def main():
    # 监视当前目录
    asyncio.create_task(watch_files("."))
    # 创建读取器
    stdin_reader = await read_from_stdin()
    while True:
        # 从命令行读取输入
        command = await stdin_reader.readline()
        # 执行命令
        procs = await asyncio.create_subprocess_shell(command)
        await procs.wait()
loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(main())
finally:
    loop.close()

来看一下效果:

结果没有问题,文件的变化都检测出来了。然后补充一点:watch 和 awatch 可以同时监听多个目录,因为第一个参数是 *paths。

我们同时监听多个目录来测试一下,先在当前目录创建两个子目录:boy 和 girl,然后分别监视它们。

输出正常,因此这两个函数可以监听任意多个目录。另外,由于目前监听的是当前目录的两个子目录,所以当前目录的文件变更就看不到了,因为它没有被监视。

以上就是这两个函数的基本用法,当然这两个函数还有其它参数:

这里简单介绍几个。

过滤器(watch_filter)

watchfiles 会监视目录的文件变化,但不是所有的文件都会记录。

watchfiles 有一个内置的过滤器,会将和业务无关的文件过滤掉,如果你还希望将其它格式的文件过滤掉,那么修改过滤器即可。

停止事件(stop_event)

监视文件的时候,迭代器是不会停止的,如果想自由控制它的结束,可以传递一个事件。

import asyncio
from watchfiles import awatch
async def watch_files(*paths, stop_event):
    async for _ in awatch(*paths, stop_event=stop_event):
        pass
    print("停止监视")
async def main():
    event = asyncio.Event()
    # 传递一个事件,准确的说,只要有 is_set 方法,任何对象都行
    asyncio.create_task(watch_files(".", stop_event=event))
    # 当 event.is_set() 为 True 的时候,停止监视
    print("is_set: ", event.is_set())
    await asyncio.sleep(3)  # sleep 3
    event.set()
    print("三秒后, is_set: ", event.is_set())
    # 等待子协程打印完毕
    await asyncio.sleep(0.1)
asyncio.run(main())
"""
is_set:  False
三秒后, is_set:  True
停止监视
"""

是否递归监视(recursive)

如果该参数为 True,那么会递归监视子目录,否则只监视顶层目录。

其它参数基本很少用,就不再赘述了,有兴趣可以自己了解一下。

到此这篇关于Python使用watchfiles实现监控目录变更的文章就介绍到这了,更多相关Python监控目录变更内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅析Python 引号、注释、字符串

    浅析Python 引号、注释、字符串

    这篇文章主要介绍了Python 引号、注释、字符串的相关知识,文中给大家提到了python中一对单引号,一对双引号,三个单双引号的区别和用法,需要的朋友可以参考下
    2019-07-07
  • 使用Python读写及压缩和解压缩文件的示例

    使用Python读写及压缩和解压缩文件的示例

    Python的os模块中提供了基本的文件读写方法,而zipfile模块则针对文件的压缩和解压缩操作,这里我们就来看一下使用Python读写及压缩和解压缩文件的示例:
    2016-07-07
  • python将excel转换为csv的代码方法总结

    python将excel转换为csv的代码方法总结

    在本篇文章里小编给大家分享了关于python如何将excel转换为csv的实例方法和代码内容,需要的朋友们学习下。
    2019-07-07
  • 详解Python3中的多重继承和混入类

    详解Python3中的多重继承和混入类

    Python原生支持多重继承,这使得我们可以从多个父类中继承属性和方法,在本文中,我们将介绍Python中多重继承的概念,并讨论在实际情况下可能遇到的坑,我们还将讨论如何使用混入类来避免这些问题,需要的朋友可以参考下
    2023-05-05
  • Python命令行参数解析工具 docopt 安装和应用过程详解

    Python命令行参数解析工具 docopt 安装和应用过程详解

    这篇文章主要介绍了Python命令行参数解析工具 docopt 安装和应用过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • python使用urllib模块和pyquery实现阿里巴巴排名查询

    python使用urllib模块和pyquery实现阿里巴巴排名查询

    这篇文章主要介绍了python库urllib及pyquery基本东西的应用,实现阿里巴巴关键词排名的查询,其中涉及到urllib代理的设置,pyquery对html文档的解析
    2014-01-01
  • python3中rsa加密算法详情

    python3中rsa加密算法详情

    这篇文章主要介绍了python3中rsa加密算法详情,rsa加密,是一种加密算法,目前而言,加密算法,是对数据、密码等进行加密,下文更多相关介绍,需要的小伙伴可以参考一下
    2022-05-05
  • 基于Python构建一个高效词汇表

    基于Python构建一个高效词汇表

    在自然语言处理(NLP)领域,构建高效的词汇表是文本预处理的关键步骤,本文将解析一个使用Python实现的n-gram词频统计工具,感兴趣的可以了解下
    2025-06-06
  • pyinstaller打包程序exe踩过的坑

    pyinstaller打包程序exe踩过的坑

    这篇文章主要介绍了pyinstaller打包exe踩过的坑,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11
  • PyQt5 在label显示的图片中绘制矩形的方法

    PyQt5 在label显示的图片中绘制矩形的方法

    今天小编就为大家分享一篇PyQt5 在label显示的图片中绘制矩形的方法,具有很好的参考价值。希望对大家有所帮助。一起跟随小编过来看看吧
    2019-06-06

最新评论