Is there a command like "watch" or "inotifywait" on the Mac?

fswatch

fswatch is a small program using the Mac OS X FSEvents API to monitor a directory. When an event about any change to that directory is received, the specified shell command is executed by /bin/bash

If you're on GNU/Linux, inotifywatch (part of the inotify-tools package on most distributions) provides similar functionality.

Update: fswatch can now be used across many platforms including BSD, Debian, and Windows.

Syntax / A Simple Example

The new way that can watch multiple paths - for versions 1.x and higher:

fswatch -o ~/path/to/watch | xargs -n1 -I{} ~/script/to/run/when/files/change.sh

Note: The number output by -o will get added to the end of the xargs command if not for the -I{}. If you do choose to use that number, place {} anywhere in your command.

The older way for versions 0.x:

fswatch ~/path/to/watch ~/script/to/run/when/files/change.sh

Installation with Homebrew

As of 9/12/13 it was added back in to homebrew - yay! So, update your formula list (brew update) and then all you need to do is:

brew install fswatch

Installation without Homebrew

Type these commands in Terminal.app

cd /tmp
git clone https://github.com/alandipert/fswatch
cd fswatch/
make
cp fswatch /usr/local/bin/fswatch

If you don't have a c compiler on your system you may need to install Xcode or Xcode command line tools - both free. However, if that is the case, you should probably just check out homebrew.

Additional Options for fswatch version 1.x

Usage:
fswatch [OPTION] ... path ...

Options:
 -0, --print0          Use the ASCII NUL character (0) as line separator.
 -1, --one-event       Exit fsw after the first set of events is received.
 -e, --exclude=REGEX   Exclude paths matching REGEX.
 -E, --extended        Use exended regular expressions.
 -f, --format-time     Print the event time using the specified format.
 -h, --help            Show this message.
 -i, --insensitive     Use case insensitive regular expressions.
 -k, --kqueue          Use the kqueue monitor.
 -l, --latency=DOUBLE  Set the latency.
 -L, --follow-links    Follow symbolic links.
 -n, --numeric         Print a numeric event mask.
 -o, --one-per-batch   Print a single message with the number of change events.
                       in the current batch.
 -p, --poll            Use the poll monitor.
 -r, --recursive       Recurse subdirectories.
 -t, --timestamp       Print the event timestamp.
 -u, --utc-time        Print the event time as UTC time.
 -v, --verbose         Print verbose output.
 -x, --event-flags     Print the event flags.

See the man page for more information.

You can use launchd for that purpose. Launchd can be configured to automatically launch a program when a file path is modified.

For example the following launchd config plist will launch the program /usr/bin/logger when the desktop folder of my user account is modified:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>logger</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/logger</string>
        <string>path modified</string>
    </array>
    <key>WatchPaths</key>
    <array>
        <string>/Users/sakra/Desktop/</string>
    </array>
</dict>
</plist>

To activate the config plist save it to the LaunchAgents folder in your Library folder as "logger.plist".

From the shell you can then use the command launchctl to activate the logger.plist by running:

$ launchctl load ~/Library/LaunchAgents/logger.plist

The desktop folder is now being monitored. Every time it is changed you should see an output in the system.log (use Console.app). To deactivate the logger.plist, run:

$ launchctl unload ~/Library/LaunchAgents/logger.plist

The configuration file above uses the WatchPaths option. Alternatively you can also use the QueueDirectories option. See the launchd man page for more information.


Facebook's watchman, available via Homebrew, also looks nice. It supports also filtering:

These two lines establish a watch on a source directory and then set up a trigger named "buildme" that will run a tool named "minify-css" whenever a CSS file is changed. The tool will be passed a list of the changed filenames.

$ watchman watch ~/src

$ watchman -- trigger ~/src buildme '*.css' -- minify-css

Notice that the path must be absolute.


You might want to take a look at (and maybe expand) my little tool kqwait. Currently it just sits around and waits for a write event on a single file, but the kqueue architecture allows for hierarchical event stacking...


watchdog is a cross-platform python API for watching files / directories, and it has builtin "tricks" tool that allows you to trigger actions (including shell commands) when events occur (including new added file, removed file and changed file).