Automated build systems, test runners, and live-reload servers are invaluable in modern development workflows, but they are often heavyweight, language-specific, or tightly coupled to particular frameworks. The entr utility takes a radically simpler approach: it watches a list of files provided via standard input and runs a specified command whenever any of those files change.
With no configuration files, no daemons, and no dependencies, entr is one of the most versatile and efficient tools for triggering commands on file changes in the Linux terminal.
What Is entr?
entr (Event Notify Test Runner) is a lightweight command-line utility that runs arbitrary commands when files change. It uses the kernel’s file notification subsystem (inotify on Linux, kqueue on macOS and BSD) to detect modifications with zero polling overhead.
Key characteristics:
- No configuration files — everything is controlled via command-line arguments and shell piping.
- Language-agnostic — works with any programming language, build system, or toolchain.
- Minimal resource usage — uses kernel-level event notifications rather than polling, consuming virtually no CPU while idle.
- Composable — follows Unix philosophy by reading file lists from standard input and executing any shell command.
Installing entr
Debian, Ubuntu, and Derivatives
sudo apt update
sudo apt install entr
Fedora, RHEL, and CentOS Stream
sudo dnf install entr
Arch Linux and Manjaro
sudo pacman -S entr
macOS (Homebrew)
brew install entr
Building from Source
git clone https://github.com/eradman/entr.git
cd entr
./configure
make
sudo make install
Verify the installation:
entr -h
Basic Usage
The fundamental pattern for using entr is:
file_list_command | entr command_to_run
entr reads a list of file paths from standard input (one per line) and monitors those files. When any of them change, it executes the specified command.
Running a Build on Source File Changes
find src/ -name '*.c' | entr make
This watches all .c files in the src/ directory. Whenever you save a C source file, entr runs make automatically.
Running Tests on Code Changes
find . -name '*.py' | entr python -m pytest
Every time you modify a Python file in the current directory tree, entr triggers the test suite.
Restarting a Server on Changes
find . -name '*.js' | entr -r node server.js
The -r flag tells entr to restart the command: it terminates the running process (sending SIGTERM) before re-executing it. This is essential for long-running processes like development servers that need to be stopped and restarted rather than simply run again.
Essential Flags and Options
-r (Restart Mode)
Terminates and restarts the command on each file change. Without this flag, entr waits for the command to finish before monitoring for the next change.
ls *.rb | entr -r ruby app.rb
-c (Clear Screen)
Clears the terminal screen before each command execution, giving you a clean view of the output:
find . -name '*.go' | entr -c go test ./...
-s (Shell Execution)
Passes the command to /bin/sh for execution, allowing you to use shell features like pipes, redirections, and command chaining:
find . -name '*.md' | entr -s 'pandoc README.md -o README.html && echo "Build complete"'
-p (Postpone)
Waits for a file change before running the command for the first time, rather than executing it immediately on startup:
find . -name '*.tex' | entr -p pdflatex document.tex
-d (Directory Watch)
Exits when a new file is added to a watched directory. This is useful in a loop that regenerates the file list and restarts entr to pick up new files:
while true; do
find . -name '*.ts' | entr -d tsc
done
When a new .ts file is created, entr exits, the loop regenerates the file list with find, and entr restarts with the updated list.
The /_ Placeholder
entr provides a special placeholder /_ that represents the path of the first file that triggered the change event. This is useful for commands that need to operate on the specific file that was modified:
find . -name '*.py' | entr python /_
When app.py is saved, entr runs python app.py. When utils.py is saved, it runs python utils.py.
find . -name '*.c' | entr -s 'echo "Changed: /_" && make'
Practical Workflow Examples
Auto-Compile LaTeX Documents
echo document.tex | entr -c pdflatex document.tex
Every time you save your LaTeX file, the PDF is regenerated automatically.
Live CSS Compilation with Sass
find styles/ -name '*.scss' | entr -s 'sass styles/main.scss:dist/main.css'
Auto-Format Code on Save
find . -name '*.py' | entr autopep8 --in-place /_
This automatically formats the Python file you just saved according to PEP 8 standards.
Run a Single Test File on Change
echo tests/test_auth.py | entr -c python -m pytest /_
Rebuild Docker Container on Dockerfile Change
echo Dockerfile | entr -s 'docker build -t myapp . && echo "Image rebuilt"'
Live Markdown Preview Generation
find docs/ -name '*.md' | entr -s 'pandoc /_ -o /tmp/preview.html && xdg-open /tmp/preview.html'
Combining entr with Other Tools
With find and Filtering
find . -name '*.ts' -not -path './node_modules/*' | entr -c npx tsc --noEmit
This excludes the node_modules directory from the watch list while monitoring all TypeScript files.
With git ls-files
git ls-files '*.go' | entr -c go test ./...
Using git ls-files instead of find ensures you only watch files tracked by Git, automatically excluding build artefacts, dependencies, and other untracked files.
With ag or rg for File Discovery
rg --files -g '*.rs' | entr cargo test
ripgrep (rg) respects .gitignore by default, making it an excellent alternative to find for generating file lists.
Handling New Files with a Watch Loop
By default, entr monitors only the files provided at startup. New files created after entr starts are not detected. The -d flag combined with a shell loop solves this:
while true; do
find . -name '*.js' -not -path './node_modules/*' | entr -d -c npm test
done
When a new .js file appears, entr -d exits, the loop regenerates the file list, and entr restarts with the new file included.
Comparison with Alternatives
- inotifywait — more powerful for complex event filtering and scripting, but requires more boilerplate.
entris simpler for the common case of “run this command when these files change.” - watchman (Facebook) — a client-server architecture designed for very large repositories. Overkill for most development workflows.
- nodemon — Node.js-specific.
entris language-agnostic. - fswatch — cross-platform but more complex.
entris simpler and more composable.
entr occupies a sweet spot between simplicity and power, making it the ideal tool for developers who want instant feedback on file changes without the overhead of framework-specific watchers or complex configuration.