Erwatch
Erwatch is an Erlang/OTP application for tracking changes in a file system. It doesn't have any dependency other than a recent version of Erlang/OTP (17+ should be OK) and optionally rebar3. It is only tested on Linux/OSX, but only Erlang/OTP standard library functions are used, so it should work on any platform where Erlang/OTP runs.
Install
Erwatch uses rebar3 to build and tests and
it is available on hex.pm. Just include the following
in your rebar.config:
{deps, [erwatch]}.
Since Erwatch is an OTP application, it must be started before
using. You can do that by including erwatch in your *.app.src or *.app file, like:
...
{applications,
[kernel,
stdlib,
erwatch
]},
...
rebar3 has a nice way of starting apps in the shell, you can try:
$ rebar3 shell --apps rewatch
Build
$ rebar3 compile
Usage
Warning! Erwatch (currently) uses polling to determine file system changes. Setting the poll interval too frequent (lower) is not recommended for wildcards which may return a large number of paths.
Erwatch supports both the synchronous/on demand and asynchronous/message based way of tracking changes.
Use erwatch:new/0 to create a watch with default options, or erwatch:new/1
to create a watch with specified options:
% on demand watch:
{ok, Watch} = erwatch:new(),
% a watch that fires every 3000 milliseconds, and sends a message on changes
{ok, AnotherWatch} = erwatch:new([{interval, 3000}]).
You can add any wildcard to the watch. See filelib:wildcard/1 documentation for available patterns. Note that, the given wildcard is relative to the current working directory.
erwatch:add_wildcard("/tmp/somedir/**/src/*.erl", Watch).
Whether you use synchronous or asynchronous watches, the change sets are
in the form of list of {Action, Path} pairs, where Action is one of
added, updated or deleted. Note that, the very first change set will
contain all resolved paths in the given wildcards.
You can change the poll interval and switch between synchronous
and asynchronous modes using erwatch:set_interval/2. An interval of
0 will make the watch synchronous and > 0 will make it asynchronous.
erwatch:set_interval(1000, Watch).
It is possible to pause an asynchronous watch using erwatch:pause/1 and
resume it using erwatch:resume/1.
erwatch:pause(Watch),
% later...
erwatch:resume(Watch).
When the time comes, you can kill a watch with erwatch:remove/1:
erwatch:remove(Watch).
Synchronous / On Demand Usage
Create a watch:
{ok, Watch} = erwatch:new().
Add one or more wildcards to the watch:
erwatch:add_wildcard("/tmp/foo1/*", Watch),
erwatch:add_wildcard("/tmp/bar2", Watch),
Assuming /tmp/foo1 is a directory and /tmp/bar2 is a file,
create, modify, delete directories, files in /tmp/foo1 and/or
create, modify, delete /tmp/bar2.
Retrieve changes:
erwatch:get_changes(Watch).
Returns e.g.:
[{added,"/tmp/foo1/myfile"},
{updated,"/tmp/foo1/yourfile"},
{updated,"/tmp/foo1/x"},
{deleted,"/tmp/bar2"}]
Asynchronous / Message Based Usage
In this mode, Erwatch will send erwatch@changes messages to the
parent process (currently the process which created the watch) on
file system changes.
Create a watch with an interval:
{ok, Watch} = erwatch:new([{interval, 1000}]).
Add one or more wildcards to the watch:
erwatch:add_wildcard("/tmp/foo1/*", Watch),
erwatch:add_wildcard("/tmp/bar2", Watch),
Assuming /tmp/foo1 is a directory and /tmp/bar2 is a file,
create, modify, delete directories, files in /tmp/foo1 and/or
create, modify, delete /tmp/bar2.
Receive changes:
loop() ->
receive
{erwatch@changes, Watch, ChangeSet} ->
% do something with `ChangeSet`
loop()
_ ->
% received some other message...
loop()
end.
The ChangeSet value might be:
[{added,"/tmp/foo1/myfile"},
{updated,"/tmp/foo1/yourfile"},
{updated,"/tmp/foo1/x"},
{deleted,"/tmp/bar2"}]
Examples
See examples/erwatch.escript for a simple file watcher.
License
Copyright (c) 2016, Yuce Tekol <yucetekol@gmail.com>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The names of its contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.