监控目录 english

Leopard, Python September 5th, 2008

最近做的一个项目比较恶心,得通过 VPN 连接到美国的服务器上去开发,键入字符的延时真是让人抓狂… 好不容易得到批准,允许本地有一份代码拷贝,但是没法搭起服务器环境,所以也仅能作为查阅用而已…
昨天小员和我突然想到了一个不错的解决方法,那就是实时监控代码目录的变化,当发现本地的代码发生变化的时候,自动拷贝到服务器上。这样我们只要在本地做修改,然后刷新浏览器就可以看到更新了。实现完后发现我们真是猪头啊,咋就没早想到这个方法呢,生生被折磨了一个多月…
小员在 Linux 下工作的,所以他很快的用 inotify 实现了一个。但是我的 Leopard 就没那么幸运了… 通过 FSEvents 监测目录只能知道目录发生了变化,但是粒度没法具体到变化的文件。而 kqueue 也差不多..
最后没办法,使用 PyObjC 通过 FSEvents 来监控目录,建立目录的文件的时间戳和大小的索引来跟踪变化的文件。虽然是恶心了点,不过还算勉强可用吧…
下面是实现的代码:

?View Code PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
from FSEvents import *
import objc
import os
import sys
import stat
 
def fsevents_callback(streamRef, clientInfo, numEvents, eventPaths, eventMasks, eventIDs):
    full_path = clientInfo
    global previous_mtimes
 
    for i in range(numEvents):
        path = eventPaths[i]
        if path[-1] == '/':
            path = path[:-1]
 
        temp_mtimes = {}
        for dirpath, dirnames, filenames in os.walk(full_path):
            for filename in filenames:
                filename = os.path.join(dirpath, filename)
                new_mtime = os.path.getmtime(filename)
                new_size = os.path.getsize(filename)
                temp_mtimes[filename] = (new_mtime, new_size)
                if filename not in previous_mtimes:                                                   
                    # Do some actions
                elif new_mtime > previous_mtimes[filename][0] and new_size != previous_mtimes[filename][1]:
                    # Do some actions
 
        previous_mtimes = temp_mtimes
 
def my_FSEventStreamCreate(path):
    streamRef = FSEventStreamCreate(kCFAllocatorDefault,
                                    fsevents_callback,
                                    path,
                                    [path],
                                    kFSEventStreamEventIdSinceNow,
                                    1.0,
                                    0)
    if streamRef is None:
        return None
 
    return streamRef
 
if __name__ == "__main__":
    full_path = '/path/to/code/'
    previous_mtimes = {}
 
    # Create a files index for target directory
    for dirpath, dirnames, filenames in os.walk(full_path):
        for filename in filenames:
            filename = os.path.join(dirpath, filename)
            previous_mtimes[filename] =  (os.path.getmtime(filename), os.path.getsize(filename))
 
    streamRef = my_FSEventStreamCreate(full_path)
    FSEventStreamScheduleWithRunLoop(streamRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)
    startedOK = FSEventStreamStart(streamRef)
 
    if not startedOK:
        exit()
 
    # Run
    CFRunLoopRun()
 
    #Stop / Invalidate / Release
    FSEventStreamStop(streamRef)
    FSEventStreamInvalidate(streamRef)
    #FSEventStreamRelease(streamRef)

Tags: ,