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