前言
在我们使用服务器的时候,有时候需要监控文件或文件夹的变化。例如,定期扫描文件夹下是否有某一类型的文件生成。
今天,我们介绍如何使用 Python 来监控文件系统。
在 Python 中,主要有两个监控文件系统的库:
- pyinotify : https://github.com/seb-m/pyinotify/wiki
pyinotify 依赖的是 Linux 的 inotify,所以它只能在 linux 中使用
- watchdog : http://pythonhosted.org/watchdog/
watchdog 是跨平台(linux、Windows、macOS)的库,对不同平台都进行了封装。但是需要安装依赖库。
通常我们使用的服务器都是 linux 系统,所以为了避免安装依赖包的麻烦,我们使用 pyinotify 库来实现我们的监控文件系统的功能。
介绍
1. 安装
- 依赖
- Linux kernel with inotify ≥ 2.6.13
- Python ≥ 2.4 or 3.x
- 安装
pipinstallpyinotify
使用
比方说,我们要监视 /tmp 及其所有子目录中的每一个新文件的创建或删除。为了简单起见,我们只在标准输出上打印每个通知的消息
现在有几种读取事件并采取适当操作的策略,其中有四种流行的模式:
- 使用 的 loop() 方法进行不间断的监视。在这种情况下,当前进程只需要专注于此任务
- 通过在 Notifier 中使用 timeout 通知程序在方便时显式调用处理方法来进行定期监视。在这种情况下,它不会阻塞您当前的线程,但是如果超时值设置得太小,也可能导致少量的响应性丢失或资源使用增加
- 生成新线程来独立地监视文件事件,此方法实例化 ThreadedNotifier
- 通过 AsyncNotifier 类进行异步监控
具体使用哪种策略主要取决于您的需要和您的环境,下面我们来实现这四种策略
共有代码
importpyinotify
#Thewatchmanagerstoresthewatchesandprovidesoperationsonwatches
wm=pyinotify.WatchManager()
#监视事件
mask=pyinotify.IN_DELETE|pyinotify.IN_CREATE
classEventHandler(pyinotify.ProcessEvent):
defprocess_IN_CREATE(self,event):
print"Creating:",event.pathname
defprocess_IN_DELETE(self,event):
print"Removing:",event.pathname
我们定义了一个 EventHandler 类,并继承一个 ProcessEvent 处理事件类。
对于某一 EVENT_TYPE,其对应的 process_EVENT_TYPE 函数将被执行。
EVENT_TYPE 包括:

1. 不加 timeout 的 Notifier
#创建事件处理对象
handler=EventHandler()
notifier=pyinotify.Notifier(wm,handler)
wdd=wm.add_watch('/tmp',mask,rec=True)
notifier.loop()
接下去就要添加需要监听的目录
wdd=wm.add_watch('/tmp',mask,rec=True)
最后,执行监听,在使用 Ctrl+C 终止之前,程序都会一直运行
notifier.loop()
2. 使用 timeout 的 Notifier
如果您希望定期检查事件而不是阻塞事件,可以使用较短的超时值来构造 Notifier
notifier=pyinotify.Notifier(wm,handler,timeout=10)
并使用这样的函数检查和处理事件
defquick_check(notifier):
assertnotifier._timeoutisnotNone,'Notifiermustbeconstructedwithashorttimeout'
notifier.process_events()
whilenotifier.check_events():#loopincasemoreeventsappearwhileweareprocessing
notifier.read_events()
notifier.process_events()
3. 使用 ThreadedNotifier 类
notifier=pyinotify.ThreadedNotifier(wm,EventHandler())
notifier.start()
wdd=wm.add_watch('/tmp',mask,rec=True)
wm.rm_watch(wdd.values())
notifier.stop()
当然,在需要的时候,我们也可以删除监视目录
ifwdd['/tmp']>0:#testifthewdisvalid,ie.if/tmpisbeingwatched,thistestisnotmandatorythough
wm.rm_watch(wdd['/tmp'])
注意 :如果 /tmp 中存在子目录,那么还会监听其子目录。如果要删除 /tmp 及其子目录的监听,可以使用 rec=True
wm.rm_watch(wdd['/tmp'],rec=True)
或者
wm.rm_watch(wdd.values())
在我们操作完目录之后,最后需要将监听事件停止
notifier.stop()
4. 使用 AsyncNotifier 类
importasyncore
notifier=pyinotify.AsyncNotifier(wm,EventHandler())
wdd=wm.add_watch('/tmp',mask,rec=True)
asyncore.loop()
这个 AsyncNotifier 类依赖于 Python 标准模块 asyncore,是轮询事件的一个替代方法。
示例
importpyinotify
importsys
classEventHandler(pyinotify.ProcessEvent):
defmy_init(self,file_object=sys.stdout):
"""
Thisisyourconstructoritisautomaticallycalledfrom
ProcessEvent.__init__(),Andextraargumentspassedto__init__()would
bedelegatedautomaticallytomy_init().
"""
self._file_object=file_object
self.sign=False
defprocess_IN_CREATE(self,event):
"""
Thismethodprocessesaspecifictypeofevent:IN_CREATE.event
isaninstanceofEvent.
"""
ifhasattr(event,'pathname')andevent.pathname.endswith('.xls'):
print('%shavebeencreated.\n'%event.pathname,file=self._file_object)
defprocess_IN_DELETE(self,event):
"""
Thismethodprocessesaspecifictypeofevent:IN_DELETE.event
isaninstanceofEvent.
"""
print('deleting:%s\n'%event.pathname,file=self._file_object)
#pass
defprocess_IN_CLOSE(self,event):
"""
Thismethodiscalledontheseevents:IN_CLOSE_WRITEand
IN_CLOSE_NOWRITE.
"""
#print('closing:%s\n'%event.pathname,file=self._file_object)
pass
defprocess_IN_CLOSE_WRITE(self,event):
"""
Thismethodprocessesaspecifictypeofevent:IN_CLOSE_WRITE
"""
pass
defprocess_default(self,event):
"""
Eventually,thismethodiscalledforallotherstypesofevents.
Thismethodcanbeusefulwhenanactionfitsallevents.
"""
#print('defaultprocessing\n',file=self._file_object)
pass
defwatching(path,exclude_path=None,rec=False,read_freq=0,timeout=None):
"""watchfilesordirectories
@args:
path:strorlistofstr,Pathtowatch,thepathcaneitherbea
fileoradirectory.Alsoacceptsasequence(list)ofpaths.
exclude_path:strorlist,predicate(booleanfunction),whichreturnsTrue
ifthecurrentpathmustbeexcludedfrombeingwatched.This
argumenthasprecedenceoverexclude_filterpassedtothe
class'constructor.
rec:Recursivelyaddwatchesfrompathonallitssubdirectories,
settoFalsebydefault(doesn'tfollowssymlinksinanycase)
read_freq:ifread_freq==0,eventsarereadasap,ifread_freqis>0,
thisthreadsleepsmax(0,read_freq-(timeout/1000))seconds.
ButiftimeoutisNoneitmaybedifferentbecausepollis
blockingwaitingforsomethingtoread.
timeout:seeread_freqabove.Ifprovided,itmustbesetinmilliseconds
"""
#InstanciateanewWatchManager(willbeusedtostorewatches)
wm=pyinotify.WatchManager()
#eventstypes
mask=pyinotify.IN_DELETE|pyinotify.IN_CREATE
#AssociatethisWatchManagerwithaNotifier(willbeusedtoreportandprocessevents).
notifier=pyinotify.Notifier(wm,EventHandler(),read_freq=read_freq,timeout=timeout)
#Addanewwatchon'path'forsomeXXX_EVENTS.
ifisinstance(path,str)orisinstance(path,list):
print("nowstartingmonitor%s."%path)
wm.add_watch(path,mask,rec=rec,exclude_filter=exclude_path)
else:
raiseValueError("the%sseemsnotvalidpath"%(path))
#Loopforeverandhandleevents.
notifier.loop()
if__name__=='__main__':
path='~/jupyter/Others/Wechat'
watching(path)
这个脚本监控目录下 .xls 类型文件的创建和任意文件删除
- 运行
nbsp;pythonwatching.py
nowstartingmonitor~/jupyter/Others/Wechat.
- 新建 xls 文件
toucha.xlsb.xlsc.txt
- 输出
~/jupyter/Others/Wechat/a.xlshavebeencreated.
~/jupyter/Others/Wechat/b.xlshavebeencreated.
- 删除文件
ls|egrep'.*xls|txt'|xargsrm
- 输出
deleting:~/jupyter/Others/Wechat/a.xls
deleting:~/jupyter/Others/Wechat/b.xls
deleting:~/jupyter/Others/Wechat/c.txt
总结
上面的脚本主要用来监听目录下面文件的创建和删除,如果想要监听其他事件,可以在脚本的基础上进行改下,并添加对应的时间处理。
同时,如果想要定时监控的话,可以设置 read_freq 和 timeout 参数。
计算公式为:
max(0,read_freq-(timeout/1000))
文件 github 地址:
https://github.com/dxsbiocc/learn/blob/main/Python/utils/watching.py