Run arbitrary commands when files change
bitbucket/overview | download 3.9
Rebuild project if sources change
$ ag -l | entr make
Rebuild project and run tests if the build was successful
$ ag -l | entr -s 'make && make test'
find(1)
or
grep(1)
in that they recognize files by their contents and are smart enough to
skip directories such as
.git
The Event Notify Test Runner is a general purpose Unix utility intended to make rapid feedback and automated testing natural and completely ordinary.
Some graphical applications such as the PostScript/PDF viewer
gv
ship with a
-watch
option with reloads the document whenever the source file is modified.
This is useful, but it is even better for applications to provide a
programmatic means of refreshing the display. The browser add-on
LiveReload
has attempted to solve this problem for web developers by injecting
JavaScript that listens on a web socket. The should not be this complex,
indeed this is all that is required:
$ ls *.css *.html | entr reload-browser Firefox
reload-browser
is a simple script which uses
xdotool
or AppleScript to send a refresh keystroke to the active tab in your
browser. Some lesser known browsers, such as
Midori
can be controlled from the command line using arguments -e Reload
.
It is not uncommon for modern web frameworks to continuously hunt for file system changes and auto-reloads when run in single threaded or standalone mode. This functionality is superfluous if the application can respond to signals. The following will instruct mupdf to reload a PDF whenever it is updated:
$ ls *.pdf | entr pkill -HUP mupdf
entr
is a zero-configuration tool with no external
build or runtime dependencies. The interface to
entr
is not only minimal, it aims to be simple enough to create a new category
of ad hoc automation. These
micro-tests
reduce keystrokes, but more importantly they emphasize the utility of
automated checks.
Tightening the edit-test feedback loop requires a tool that is tuned for one task. inotifywait is lightweight, but it only works on Linux, and does not provide a direct means of saying “run this command if any of these files change”. In practice scripting with inotify-tools is difficult because there are a number of significant conditions to contend with:
entr
deals with this by closing the old file descriptor and reopening it
using the same pathname. Since there is a small delay while the new file
is renamed, we must wait for the new file to appear before running the
supplied command and attempting to watch the new file.
entr
allows you to safely edit files while tests are running without a
repeated invocation of the utility.
entr
repeatedly probes for subsequent events, and only executes the utility
when the kernel returns with no results after a short timeout.
IN_MOVE_SELF|IN_DELETE_SELF
instead of
IN_MODIFY|IN_CLOSE_WRITE
and on BSD kqueue may report
NOTE_RENAME|NOTE_DELETE
instead of
NOTE_WRITE|NOTE_EXTEND
.
entr
retries the execution.
entr
consolidates events over 50ms before responding.
backup
option is set. To deal with this events must be explicitly unregistered
to prevent the kernel from tracking changes to backup files.
entr
adheres to the principle of separation
of concerns, yet the reload
(-r
)
option was added to solve a common use case that would otherwise require
some careful scripting:
$ ls *.rb | entr -r ruby main.rb
This will,
The 3.1 release further tuned this behavior by setting a process group to ensure that all child processes receive a signal. This enables you to use a startup script without having to write custom signal in handlers.
Other special-purpose flags were added because they reduce highly
repetitive actions or reduce friction. One of the most repetitive actions
was to clear the screen before running tests; hence the
-c
flag:
$ ls -d * | entr -c ./test.sh
The special
/_
argument (somewhat analogous to
$_
in Perl) provides a quick way to refer to the first file that changed.
When a single file is listed this is a handy way to avoid typing a
pathname twice:
$ ls *.sql | entr psql -f /_
In the 2.9 release, a directory watch option (-d
) was added to react to events when a new file is added to a
directory. It was determined early on that
entr
would not implement it's own file search syntax, relying on standard Unix
tools instead. The implication of this is that if a new file appears it
must exit and allow an external shell loop to rescan the file system.
One
way to implement this feature would be to simply require the users to list
directories, but
entr
will infer the directories if they aren't not listed explititly
$ while true; do > ls -d src/*.py | entr -d ./setup.py > done
With the help of the treedelta utility it is also easy possible to see what files were added or removed from a directory.
I have written as if incorporating automated responses can be accomplished without special demands on the Unix development environment, but in practice the ability to split a window into multiple panes is the key to making this workflow efficient. A window manager such as i3 or the terminal multiplexer tmux enables you to quickly split the screen so that you can see the results as you work.
tmux
steps automation up to the next level by enabling you to control
applications in other panes via keystrokes. This combination can be wired
up in any number of ways to create some very interesting auto-responders.
Consider the following
vimdiff -R
is started in the first pane to compare two
files containing similar HTTP headers
entr
is started in the second pane, and watches for
updates to these two files
site1.txt
or
site2.txt
is
updated
tmux send-keys -t .0
is invoked to send keystrokes to
the pane running Vim
":windo e"
is the Vim command for “read the file
again in each window”
C-m
commits this action by sending the
Return
key
With this mechanism in place we can fetch and compare headers using any tool capable of printing output or writing to a file—no plugins or specialized functionality required.
Last updated on November 02, 2017
Send questions or comments to
[email protected]