Aller au contenu

entr Cheat Sheet

Overview

entr is a small, focused Unix utility that watches a list of files and runs a command when any of them change. It follows the Unix philosophy of doing one thing well — it reads file paths from stdin and executes commands on modification. No configuration files, no daemons, no complex setup.

entr works on Linux, macOS, FreeBSD, and other Unix-like systems. It uses kqueue or inotify for efficient change detection without polling. Its simplicity makes it ideal for quick development feedback loops like auto-running tests, rebuilding projects, or restarting servers on file changes.

Installation

# macOS
brew install entr

# Ubuntu/Debian
sudo apt install entr

# Fedora
sudo dnf install entr

# Arch Linux
sudo pacman -S entr

# FreeBSD
pkg install entr

# From source
curl -O https://eradman.com/entrproject/code/entr-5.6.tar.gz
tar xzf entr-5.6.tar.gz
cd entr-5.6
./configure
make
sudo make install

# Verify
entr -h

Core Usage

FlagDescription
-cClear the screen before running the command
-dTrack directories for new file additions
-pPostpone first execution until a file changes
-rRestart persistent processes (sends SIGTERM)
-sEvaluate command using $SHELL -c
-zExit after command returns non-zero
-nNon-interactive mode (no TTY)

Basic Patterns

# Run make when any .c file changes
ls *.c | entr make

# Run tests when source changes
find src -name '*.py' | entr pytest

# Clear screen and run
find . -name '*.go' | entr -c go test ./...

# Use shell features with -s
ls *.md | entr -s 'pandoc README.md -o README.html && echo "Done"'

# Postpone until change (don't run immediately)
find . -name '*.rs' | entr -cp cargo build

Common Workflows

Auto-Test

# Python tests
find . -name '*.py' | entr -c pytest -x

# Go tests
find . -name '*.go' | entr -c go test ./...

# Node.js tests
find src test -name '*.ts' | entr -c npx jest

# Ruby tests
find . -name '*.rb' | entr -c bundle exec rspec

# Rust tests
find src -name '*.rs' | entr -c cargo test

Auto-Build

# C/C++ project
find src -name '*.c' -o -name '*.h' | entr make

# LaTeX document
echo thesis.tex | entr pdflatex thesis.tex

# Sass/CSS
find styles -name '*.scss' | entr -s 'sass styles/main.scss dist/style.css'

# TypeScript
find src -name '*.ts' | entr -c npx tsc --noEmit

# Hugo/Jekyll static site
find content -name '*.md' | entr -s 'hugo build'

Auto-Restart Servers

# Restart Node.js server
find src -name '*.js' | entr -r node server.js

# Restart Python Flask app
find . -name '*.py' | entr -r python app.py

# Restart Go server
find . -name '*.go' | entr -r go run main.go

# Restart with cleanup
find . -name '*.py' | entr -rs 'kill $CHILD 2>/dev/null; python manage.py runserver'

Watch for New Files

# -d flag: exit when new files appear in watched dirs
# Wrap in while loop to restart with new file list
while true; do
  find src -name '*.js' | entr -d npm run build
done

# Same pattern for tests
while true; do
  find . -name '*.py' | entr -d pytest
done

Configuration

Filtering Files

# Exclude directories
find . -name '*.py' -not -path './venv/*' -not -path './.tox/*' | entr pytest

# Using fd (faster alternative to find)
fd -e py | entr -c pytest

# Using git ls-files (only tracked files)
git ls-files '*.ts' '*.tsx' | entr -c npm test

# Specific files only
echo "src/main.py\nsrc/config.py" | entr python src/main.py

Combining with Other Tools

# With ag/ripgrep to find files containing pattern
rg --files -g '*.py' | entr -c pytest

# With fd for globbing
fd -e go -E vendor | entr -c go test ./...

# Pipe changed filename to command (/_)
ls *.c | entr -s 'gcc /_ -o test && ./test'

# Process substitution
entr -c make < <(find src -name '*.c')

Advanced Usage

Using the /_ Placeholder

# /_ is replaced with the changed file path
ls *.py | entr -c python /_

# Compile only the changed file
ls *.c | entr -s 'gcc /_ -o ${_%.c} && echo "Built: /_"'

# Lint only the changed file
find . -name '*.py' | entr -c flake8 /_

# Format only the changed file
find . -name '*.go' | entr gofmt -w /_

Complex Workflows

# Multi-step build
find src -name '*.ts' | entr -s '
  echo "=== Building ==="
  npx tsc --noEmit && \
  npx jest --bail && \
  echo "=== All checks passed ==="
'

# Notify on completion (macOS)
find . -name '*.rs' | entr -s '
  cargo test 2>&1 && \
  osascript -e "display notification \"Tests passed\" with title \"Cargo\""
'

# With tmux: send keys to another pane
find . -name '*.py' | entr -s 'tmux send-keys -t 1 "pytest" Enter'

Non-Interactive Mode

# For CI/scripts (no TTY required)
find . -name '*.py' | entr -n pytest

# Exit on first failure
find . -name '*.py' | entr -zn pytest

# In Docker containers
find /app -name '*.py' | entr -rn python /app/server.py

Troubleshooting

IssueSolution
entr: Too many files listedReduce file list or increase OS limits
New files not detectedUse -d flag in a while true loop
Server not restarting cleanlyUse -r flag; it sends SIGTERM to child
Command runs with stale dataAdd -c to clear screen; check for caching
Not working in DockerUse -n flag for non-interactive mode
Immediate exit without runningEnsure stdin has file paths; check pipes
# Debug: check what files are being watched
find . -name '*.py' | wc -l

# Check inotify limits (Linux)
cat /proc/sys/fs/inotify/max_user_watches

# Increase limit
echo "fs.inotify.max_user_watches=524288" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

# Test with single file
echo "main.py" | entr -c python main.py