fswatch: Ruby and FSEvents
The latest script I added to the dld-tools project (in my public Mercurial repositories) is called
fswatch. The script, as of this writing, is available here. Click on “raw” to get a file you can save and run. Check the repository for newer updates.
fswatch is a Ruby script that uses the built-in RubyCocoa bridge to access the new File System Events (FSEvents) framework added in Leopard and watches for filesystem modifications while a command is run. Since FSEvents can efficiently watch a directory hierarchy, you can see what files a command touches or spews anywhere on the filesystem. One use case would be to see what a
make install actually does. Here’s an example installing the GNU Hello program:
% sudo fswatch -o /tmp/hello-install.txt make install % cat /tmp/hello-install.txt / tmp /private/tmp/ . hello-install.txt /usr/local/bin/ . hello /usr/local/share/ . info /usr/local/share/info/ . .. dir hello.info /usr/local/share/man/man1/ . hello.1
From the output, you can see that a binary, info documentation, and a man page were all installed at their standard locations in
/usr/local. The implementation of this script is fairly simple. I used the
watcher.rb example code as a starting point, which is installed with Xcode at:
Here’s the pseudo-code for the meat of
startId = FSEventsGetCurrentEventId() run_command stream = FSEventStreamCreate(pathsToWatch => ["/"], sinceWhen => startId) FSEventStreamScheduleWithRunLoop(stream) FSEventStreamStart(stream) FSEventStreamFlushSync(stream)
The call to
FSEventsGetCurrentEventId gets the event ID just before the command is executed. When the stream is created after the command is executed, you can use this as the
sinceWhen argument to get all file system events from the pre-command state. The
FSEventStreamFlushSync call gets all the file system events that have happened since before the command was run. It calls a callback setup in
FSEventStreamCreate. See the API documentation for details on the FSEvents functions.
The only tricky part is that FSEvents only tells you directories that have been updated, not files. The script fudges this by scanning each directory that FSEvents gives it for files that have been modified since the command ran. This means, that there’s no way to list removed files. Another limitation is that FSEvents tells you all file system activity. If some other application happens to be writing to, say,
~/Library/Caches during execution of the command, then
fswatch will pick this up, too. But, even with these limitations, I think this will be a fairly useful script.