Python使用watchfiles實現(xiàn)監(jiān)控目錄變更
在工作中難免會碰到這樣的需求,監(jiān)控指定目錄,如果該目錄下發(fā)生文件變更,那么進行一系列的處理。而如何監(jiān)視一個目錄,就是我們本次探討的主題。
監(jiān)視目錄我們可以使用 watchfiles 模塊,該模塊不僅簡單,而且性能也不錯。主要原因是,和底層文件系統(tǒng)交互的代碼是基于 Rust 編寫的,所以性能是有保證的。
通過 pip install watchfiles 安裝之后,我們來看看它的用法。
from?watchfiles?import?watch #?當(dāng)前目錄為?/Users/satori/Desktop/project for?change?in?watch("."): ????print(change)
我們執(zhí)行此程序,會處于阻塞狀態(tài),并持續(xù)監(jiān)聽指定目錄的變化。然后我們在當(dāng)前目錄創(chuàng)建幾個文件,看看效果。
創(chuàng)建一個 data.txt 文本文件,程序輸出如下:
{(<Change.added: 1>, '/Users/satori/Desktop/project/data.txt')}
返回的是一個集合,目前只涉及一個文件的變更,所以集合里面只有一個元素。而集合里面存儲的都是元組,元組的第一個元素表示操作類型,總共有三種:分別是增加、修改和刪除。
元組的第二個參數(shù)就是具體的文件路徑,因此程序的輸出就告訴我們,當(dāng)前目錄新增了一個 data.txt。
再創(chuàng)建一個 txt_files 目錄,程序輸出如下:
{(<Change.added: 1>, '/Users/satori/Desktop/project/txt_files')}
不管是目錄文件還是文本文件,都屬于文件,所以輸出是一樣的。如果想知道新增的到底是目錄還是普通文件,那么還需要通過 os 模塊檢測一下。
我們在 txt_files 目錄中創(chuàng)建一個 data.txt,程序輸出如下:
{(<Change.added: 1>, '/Users/satori/Desktop/project/txt_files/data.txt')}
所以 watch 函數(shù)監(jiān)聽的不僅是指定目錄,其內(nèi)部的遞歸子目錄也會一并監(jiān)聽。
問題來了,當(dāng)前目錄下存在一個 data.txt 文件和一個 txt_files 目錄,而 txt_files 目錄也存在一個 data.txt。那么如果將當(dāng)前目錄的 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 里面的文件被替換掉了,所以相當(dāng)于先被刪除、然后重新創(chuàng)建。而當(dāng)前目錄中的 data.txt 被移走了,因此相當(dāng)于被刪除了。
然后我們再通過 mkdir -p a/b/c 同時創(chuàng)建多級目錄,程序輸出如下:
{(<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 函數(shù)之外,還有一個 awatch。這兩者的作用是一樣的,參數(shù)也全部一樣,只不過 awatch 需要和協(xié)程搭配,我們舉個例子。
import?sys import?asyncio from?asyncio?import?StreamReader from?watchfiles?import?awatch,?Change #?監(jiān)視指定目錄 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 函數(shù),因為它是同步阻塞調(diào)用 #?這種調(diào)用在協(xié)程當(dāng)中是大忌,會阻塞整個線程,我們需要改造成異步模式 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?函數(shù)的具體細節(jié)暫時不用太關(guān)注 #?只需要知道它能異步讀取命令行即可,關(guān)于這方面的內(nèi)容后續(xù)會介紹 #?然后定義主協(xié)程 async?def?main(): ????#?監(jiān)視當(dāng)前目錄 ????asyncio.create_task(watch_files(".")) ????#?創(chuàng)建讀取器 ????stdin_reader?=?await?read_from_stdin() ????while?True: ????????#?從命令行讀取輸入 ????????command?=?await?stdin_reader.readline() ????????#?執(zhí)行命令 ????????procs?=?await?asyncio.create_subprocess_shell(command) ????????await?procs.wait() loop?=?asyncio.get_event_loop() try: ????loop.run_until_complete(main()) finally: ????loop.close()
來看一下效果:
結(jié)果沒有問題,文件的變化都檢測出來了。然后補充一點:watch 和 awatch 可以同時監(jiān)聽多個目錄,因為第一個參數(shù)是 *paths。
我們同時監(jiān)聽多個目錄來測試一下,先在當(dāng)前目錄創(chuàng)建兩個子目錄:boy 和 girl,然后分別監(jiān)視它們。
輸出正常,因此這兩個函數(shù)可以監(jiān)聽任意多個目錄。另外,由于目前監(jiān)聽的是當(dāng)前目錄的兩個子目錄,所以當(dāng)前目錄的文件變更就看不到了,因為它沒有被監(jiān)視。
以上就是這兩個函數(shù)的基本用法,當(dāng)然這兩個函數(shù)還有其它參數(shù):
這里簡單介紹幾個。
過濾器(watch_filter)
watchfiles 會監(jiān)視目錄的文件變化,但不是所有的文件都會記錄。
watchfiles 有一個內(nèi)置的過濾器,會將和業(yè)務(wù)無關(guān)的文件過濾掉,如果你還希望將其它格式的文件過濾掉,那么修改過濾器即可。
停止事件(stop_event)
監(jiān)視文件的時候,迭代器是不會停止的,如果想自由控制它的結(jié)束,可以傳遞一個事件。
import?asyncio from?watchfiles?import?awatch async?def?watch_files(*paths,?stop_event): ????async?for?_?in?awatch(*paths,?stop_event=stop_event): ????????pass ????print("停止監(jiān)視") async?def?main(): ????event?=?asyncio.Event() ????#?傳遞一個事件,準(zhǔn)確的說,只要有 is_set 方法,任何對象都行 ????asyncio.create_task(watch_files(".",?stop_event=event)) ????#?當(dāng)?event.is_set()?為?True?的時候,停止監(jiān)視 ????print("is_set:?",?event.is_set()) ????await?asyncio.sleep(3)??#?sleep?3 ????event.set() ????print("三秒后,?is_set:?",?event.is_set()) ????#?等待子協(xié)程打印完畢 ????await?asyncio.sleep(0.1) asyncio.run(main()) """ is_set:??False 三秒后,?is_set:??True 停止監(jiān)視 """
是否遞歸監(jiān)視(recursive)
如果該參數(shù)為 True,那么會遞歸監(jiān)視子目錄,否則只監(jiān)視頂層目錄。
其它參數(shù)基本很少用,就不再贅述了,有興趣可以自己了解一下。
到此這篇關(guān)于Python使用watchfiles實現(xiàn)監(jiān)控目錄變更的文章就介紹到這了,更多相關(guān)Python監(jiān)控目錄變更內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python實現(xiàn)鳶尾花三種聚類算法(K-means,AGNES,DBScan)
這篇文章主要介紹了python實現(xiàn)鳶尾花三種聚類算法(K-means,AGNES,DBScan),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06python腳本調(diào)用iftop 統(tǒng)計業(yè)務(wù)應(yīng)用流量的思路詳解
這篇文章主要介紹了python腳本調(diào)用iftop 統(tǒng)計業(yè)務(wù)應(yīng)用流量的思路詳解,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-10-10python 將html轉(zhuǎn)換為pdf的幾種方法
這篇文章主要介紹了python 將html轉(zhuǎn)換為pdf的幾種方法,幫助大家更好的理解和使用python,感興趣的朋友可以了解下2020-12-12PyTorch?TensorFlow機器學(xué)習(xí)框架選擇實戰(zhàn)
這篇文章主要為大家介紹了PyTorch?TensorFlow機器學(xué)習(xí)框架選擇實戰(zhàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10python array中關(guān)于[a,b,c]的使用方式
這篇文章主要介紹了python array中關(guān)于[a,b,c]的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-02-02