entr – run arbitrary commands when files change

✍️ Written on 2021-05-27 in 875 words.
Part of cs software-development Linux

Motivation

Sometimes, I want to implement tools which cover some of my usecases. Sometimes, these tools have been implemented already in a mature manner. entr is a tool few people know, but is fundamental to my work.

Idea

Let’s us consider a simple Makefile script. Let us store doc.tex:

\documentclass{article}
\usepackage{fontspec}
\usepackage{geometry}
\geometry{a4paper}
\begin{document}
Hello World!
\end{document}

Furthermore, Makefile:

all:
	lualatex --interaction=nonstopmode doc.tex

Now, let us run entr:

ls doc.tex | entr make

The result of this non-terminating command is that lualatex will be run (through Make) whenever doc.tex changes. In this case, make is called utility. Actually, you could also invoke (without Make) …

ls doc.tex | entr -- lualatex --interaction=nonstopmode doc.tex

Fundamentals

  • entr is the obvious usecase for the inotify API. inotifywait (via inotify-tools) is another tool. Originally, I wrote a python script implementing entr with the inotify package.

  • Many servers in debug mode have interfaces which restart upon file changes. Older tools don’t. entr bridges the gap.

Examples (via documentation)

  • Rebuild a project if source files change, limiting output to the first 20 lines:

    find src/ | entr -s 'make | sed 20q'
  • Launch and auto-reload a node.js server:

    ls *.js | entr -r node app.js
  • Clear the screen and run a query after the SQL script is updated:

    echo my.sql | entr -cp psql -f /_
  • Rebuild project if a source file is modified or added to the src/ directory:

    while true; do ls src/*.rb | entr -d make; done
  • Self-terminate after a file is updated

    ls * | entr -p 'kill $PPID'

Details

  • The list of files to watch must be provided as stdin input (e.g. via a pipe). I don’t know how it behaves for more complex usecases like newlines in the file’s basename. find can be a helpful utility to generate filenames of a tree.

  • entr will run the utility whenever space is pressed. q terminates entr.

  • entr will run once immediately when invoked. -p disables this behavior.

  • entr -c run clear before an update.

  • entr -n disables interactivity.

  • Inotify does not support recursively watching directories, meaning that a separate inotify watch must be created for every subdirectory. entr supports this usecase, but I don’t know about limitations (like e.g. directory sizes).

systemd file to run sphinx whenever a source file changes

Just as an example: I use this systemd unit file to regenerate sphinx output whenever a source file changes:

[Unit]
Description = My Sphinx for personal notes

[Service]
User = meisterluk
Type = simple
PIDFile = /run/my-sphinx.pid
ExecStart = sh -c 'find /home/meisterluk/my-sphinx/source -type f | entr -d -n /home/meisterluk/infrastructure/venv-sphinx/bin/sphinx-build -b html /home/meisterluk/my-sphinx/source /home/meisterluk/my-sphinx/build/html'
StandardOutput = syslog+console
Restart = always
RestartSec = 1s
StartLimitInterval = 60
StartLimitBurst = 10

[Install]
WantedBy = multi-user.target

I don’t think this is the best configuration, but it works for my usecases. The sh command utilizes the appropriate python virtual environment.