• Stars
    star
    3,151
  • Rank 14,263 (Top 0.3 %)
  • Language
    Go
  • License
    MIT License
  • Created over 11 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Run a command when files change

Reflex

Reflex is a small tool to watch a directory and rerun a command when certain files change. It's great for automatically running compile/lint/test tasks and for reloading your application when the code changes.

A simple example

# Rerun make whenever a .c file changes
reflex -r '\.c$' make

Installation

You can download binaries from the Releases page.

To compile from source, you'll need Go 1.13+ installed.

If you have Go 1.16 or later, you can download and install the latest module version directly with

go install github.com/cespare/reflex@latest

Reflex is only tested on Linux and macOS.

Usage

The following is given by running reflex -h:

Usage: reflex [OPTIONS] [COMMAND]

COMMAND is any command you'd like to run. Any instance of {} will be replaced
with the filename of the changed file. (The symbol may be changed with the
--substitute flag.)

OPTIONS are given below:
      --all=false:
            Include normally ignored files (VCS and editor special files).
  -c, --config="":
            A configuration file that describes how to run reflex
            (or '-' to read the configuration from stdin).
  -d, --decoration="plain":
            How to decorate command output. Choices: none, plain, fancy.
  -g, --glob=[]:
            A shell glob expression to match filenames. (May be repeated.)
  -G, --inverse-glob=[]:
            A shell glob expression to exclude matching filenames.
            (May be repeated.)
  -R, --inverse-regex=[]:
            A regular expression to exclude matching filenames.
            (May be repeated.)
      --only-dirs=false:
            Only match directories (not files).
      --only-files=false:
            Only match files (not directories).
  -r, --regex=[]:
            A regular expression to match filenames. (May be repeated.)
  -e, --sequential=false:
            Don't run multiple commands at the same time.
  -t, --shutdown-timeout=500ms:
            Allow services this long to shut down.
  -s, --start-service=false:
            Indicates that the command is a long-running process to be
            restarted on matching changes.
      --substitute="{}":
            The substitution symbol that is replaced with the filename
            in a command.
  -v, --verbose=false:
            Verbose mode: print out more information about what reflex is doing.

Examples:

    # Print each .txt file if it changes
    $ reflex -r '\.txt$' echo {}

    # Run 'make' if any of the .c files in this directory change:
    $ reflex -g '*.c' make

    # Build and run a server; rebuild and restart when .java files change:
    $ reflex -r '\.java$' -s -- sh -c 'make && java bin/Server'

Overview

Reflex watches file changes in the current working directory and re-runs the command that you specify. The flags change what changes cause the command to be rerun and other behavior.

Patterns

You can specify files to match using either shell glob patterns (-g) or regular expressions (-r). If you don't specify either, reflex will run your command after any file changes. (Reflex ignores some common editor and version control files; see Ignored files, below.)

You can specify inverse matches by using the --inverse-glob (-G) and --inverse-regex (-R) flags.

If you specify multiple globs/regexes (e.g. -r foo -r bar -R baz -G x/*/y), only files that match all patterns and none of the inverse patterns are selected.

The shell glob syntax is described here, while the regular expression syntax is described here.

The path that is matched against the glob or regular expression does not have a leading ./. For example, if there is a file ./foobar.txt that changes, then it will be matched by the regular expression ^foobar. If the path is a directory, it has a trailing /.

--start-service

The --start-service flag (short version: -s) inverts the behavior of command running: it runs the command when reflex starts and kills/restarts it each time files change. This is expected to be used with an indefinitely-running command, such as a server. You can use this flag to relaunch the server when the code is changed.

Substitution

Reflex provides a way for you to determine, inside your command, what file changed. This is via a substitution symbol. The default is {}. Every instance of the substitution symbol inside your command is replaced by the filename.

As a simple example, suppose you're writing Coffeescript and you wish to compile the CS files to Javascript when they change. You can do this with:

reflex -r '\.coffee$' -- coffee -c {}

In case you need to use {} for something else in your command, you can change the substitution symbol with the --substitute flag.

Configuration file

What if you want to run many watches at once? For example, when writing web applications I often want to rebuild/rerun the server when my code changes, but also build SCSS and Coffeescript when those change as well. Instead of running multiple reflex instances, which is cumbersome (and inefficient), you can give reflex a configuration file.

The configuration file syntax is simple: each line is a command, and each command is composed of flags and arguments -- just like calling reflex but without the initial reflex. Lines that start with # are ignored. Commands can span multiple lines if they're \-continued, or include multi-line strings. Here's an example:

# Rebuild SCSS when it changes
-r '\.scss$' -- \
   sh -c 'sass {} `basename {} .scss`.css'

# Restart server when ruby code changes
-sr '\.rb$' -- \
    ./bin/run_server.sh

If you want to change the configuration file and have reflex reload it on the fly, you can run reflex inside reflex:

reflex -s -g reflex.conf -- reflex -c reflex.conf

This tells reflex to run another reflex process as a service that's restarted whenever reflex.conf changes.

--sequential

When using a config file to run multiple simultaneous commands, reflex will run them at the same time (if appropriate). That is, a particular command can only be run once a previous run of that command finishes, but two different commands may run at the same time. This is usually what you want (for speed).

As a concrete example, consider this config file:

-- sh -c 'for i in `seq 1 5`; do sleep 0.1; echo first; done'
-- sh -c 'for i in `seq 1 5`; do sleep 0.1; echo second; done'

When this runs, you'll see something like this:

[01] second
[00] first
[01] second
[00] first
[00] first
[01] second
[01] second
[00] first
[01] second
[00] first

Note that the output is interleaved. (Reflex does ensure that each line of output is not interleaved with a different line.) If, for some reason, you need to ensure that your commands don't run at the same time, you can do this with the --sequential (-e) flag. Then the output would look like (for example):

[01] second
[01] second
[01] second
[01] second
[01] second
[00] first
[00] first
[00] first
[00] first
[00] first

Decoration

By default, each line of output from your command is prefixed with something like [00], which is simply an id that reflex assigns to each command. You can use --decoration (-d) to change this output: --decoration=none will print the output as is; --decoration=fancy will color each line differently depending on which command it is, making it easier to distinguish the output.

Ignored files

Reflex ignores a variety of version control and editor metadata files by default. If you wish for these to be included, you can provide reflex with the --all flag.

You can see a list of regular expressions that match the files that reflex ignores by default here.

Notes and Tips

If you don't use -r or -g, reflex will match every file.

Reflex only considers file creation and modification changes. It does not report attribute changes nor deletions.

For ignoring directories, it's easiest to use a regular expression: -R '^dir/'.

Many regex and glob characters are interpreted specially by various shells. You'll generally want to minimize this effect by putting the regex and glob patterns in single quotes.

If your command has options, you'll probably need to use -- to separate the reflex flags from your command flags. For example: reflex -r '.*\.txt' -- ls -l.

If you're going to use shell things, you need to invoke a shell as a parent process:

reflex -- sh -c 'sleep 1 && echo {}'

If your command is running with sudo, you'll need a passwordless sudo, because you cannot enter your password in through reflex.

It's not difficult to accidentally make an infinite loop with certain commands. For example, consider this command: reflex -r '\.txt' cp {} {}.bak. If foo.txt changes, then this will create foo.txt.bak, foo.txt.bak.bak, and so forth, because the regex \.txt matches each file. Reflex doesn't have any kind of infinite loop detection, so be careful with commands like cp.

The restart behavior works as follows: if your program is still running, reflex sends it SIGINT; after 1 second if it's still alive, it gets SIGKILL. The new process won't be started up until the old process is dead.

Batching

Part of what reflex does is apply some heuristics to batch together file changes. There are many reasons that files change on disk, and these changes frequently come in large bursts. For instance, when you save a file in your editor, it probably makes a tempfile and then copies it over the target, leading to several different changes. Reflex hides this from you by batching some changes together.

One thing to note, though, is that the the batching is a little different depending on whether or not you have a substitution symbol in your command. If you do not, then updates for different files that all match your pattern can be batched together in a single update that only causes your command to be run once.

If you are using a substitution symbol, however, each unique matching file will be batched separately.

Argument list splitting

When you give reflex a command from the commandline (i.e., not in a config file), that command is split into pieces by whatever shell you happen to be using. When reflex parses the config file, however, it must do that splitting itself. For this purpose, it uses this library which attempts to match sh's argument splitting rules.

This difference can lead to slightly different behavior when running commands from a config file. If you're confused, it can help to use --verbose (-v) which will print out each command as interpreted by reflex.

Open file limits

Reflex currently must hold an open file descriptor for every directory it's watching, recursively. If you run reflex at the top of a big directory tree, you can easily run into file descriptor limits. You might see an error like this:

open some/path: too many open files

There are several things you can do to get around this problem.

  1. Run reflex in the most specific directory possible. Don't run reflex -g path/to/project/*.c ... from $HOME; instead run reflex in path/to/project.
  2. Ignore large subdirectories. Reflex already ignores, for instance, .git/. If you have other large subdirectories, you can ignore those yourself: reflex -R '^third_party/' ... ignores everything under third_party/ in your project directory.
  3. Raise the fd limit using ulimit or some other tool. On some systems, this might default to a restrictively small value like 256.

See issue #6 for some more background on this issue.

The competition

Why you should use reflex instead

  • Reflex has no dependencies. No need to install Ruby or anything like that.
  • Reflex uses an appropriate file watching mechanism to watch for changes efficiently on your platform.
  • Reflex gives your command the name of the file that changed.
  • No DSL to learn -- just give it a shell command.
  • No plugins.
  • Not tied to any language, framework, workflow, or editor.

Authors

More Repositories

1

xxhash

A Go implementation of the 64-bit xxHash algorithm (XXH64)
Go
1,597
star
2

vim-toml

Vim syntax for TOML
Vim Script
624
star
3

percpu

Best-effort CPU-local sharded values for Go
Go
190
star
4

ryu

A Go implementation of the Ryu algorithm for converting floating-point numbers to strings
Go
91
star
5

pastedown

Easy-to-use markdown-formatting pastebin
Go
90
star
6

prettybench

Format Go's benchmarking output
Go
87
star
7

go-smaz

A pure Go implementation of the smaz compression library for short strings.
Go
77
star
8

mph

Minimal perfect hashing in Go
Go
71
star
9

deplist

List the external dependencies of a Go package.
Go
63
star
10

gost

A largely compatible statsd daemon implementation in Go.
Go
55
star
11

goclj

Clojure parsing in Go
Go
37
star
12

go-tetris

A console-based tetris implementation in go.
Go
31
star
13

frosty

A raytracer, for fun
Go
24
star
14

saturday

A small SAT solver in Go
Go
23
star
15

markdownd

A markdown renderer and server for your command line.
Go
19
star
16

misc

Experiments and bad ideas
Go
19
star
17

stats

A commandline utility for summary statistics
Go
18
star
18

discoball

A simple stream filter to highlight patterns
Ruby
17
star
19

vim-sbd

Smart buffer closing. Mirror of repo by Orphée Lafond-Lummis.
Vim Script
17
star
20

vim-config

My personal vim configuration.
Vim Script
17
star
21

permute

Permutations for Go
Go
16
star
22

sub

Find/replace across files.
Go
14
star
23

vim-go-templates

Vim syntax highlighting for Go templates.
Vim Script
13
star
24

cbor

A Go implementation of CBOR (compact binary object representation).
Go
13
star
25

kvcache

An expiring key/value cache with a Redis interface
Go
11
star
26

go-localpath

Shell script for managing Go projects with vendored dependencies.
Shell
11
star
27

carlisle

Window control shortcuts for EWMH-compliant X window managers
Go
10
star
28

go-trie

A pure Go trie (prefix tree) implementation.
Go
8
star
29

dotfiles

Personal dotfiles, vim config, little scripts, etc.
Shell
8
star
30

pinion

A lightweight asset management system for Rack
Ruby
8
star
31

flake

Find test flakes
Go
6
star
32

playctrl

Control Google Play in Chrome via global shortcuts.
Go
6
star
33

boggle-solver

A simple boggle solver written for fun.
Ruby
6
star
34

ruby-dedent

A tiny addition to add a dedent method for Ruby strings
Ruby
6
star
35

vjde

Mirror of http://www.vim.org/scripts/script.php?script_id=1213
Vim Script
6
star
36

goproc

Parsing and tools for the Linux /proc pseudo-file system
Go
5
star
37

cp

File copying for Go
Go
5
star
38

tabular

Write tabular text
Go
5
star
39

sitkin

A static site generator.
Go
5
star
40

next

Experimental packages using generics
Go
5
star
41

diff

Go library for diffing files and readers.
Go
5
star
42

subcmd

A minimal Go package for CLI interfaces with sub-commands
Go
4
star
43

hprofviz

HProf output visualizer
Go
4
star
44

tsprefix

Prepend timestamps to each line of standard input
Go
4
star
45

jquery-ajax-retry

A simple jquery 'plugin' to retry ajax requests.
CoffeeScript
4
star
46

git-scripts

My personal git helper scripts
Ruby
4
star
47

assetserver

Go file server for web assets
Go
4
star
48

cron

A Go implementation of the cron scheduling format
Go
4
star
49

memstats

Go
4
star
50

tumblestone

Quick solver for tumblestone
Go
3
star
51

lp

list processes
Go
3
star
52

vim-bclose

Clone of bclose script.
Vim Script
3
star
53

mxml.vim

MXML syntax highlighting
Vim Script
3
star
54

flagconf

Easy TOML + flags configuration for Go programs.
Go
3
star
55

shadow

Expose Graphite metrics with an HTTP API
Go
3
star
56

go-apachelog

A logger for go servers that uses the apache common log format.
Go
3
star
57

gomaxprocs

A slightly better interface for accessing Go's runtime.GOMAXPROCS.
Go
3
star
58

deep_hash

A very small Ruby gem to manage multi-level default hashes.
Ruby
3
star
59

aoc2020

Advent of Code 2020
Go
3
star
60

glp

A code and dependency organization tool for Go
Go
2
star
61

argf

A Go implementation of Ruby's ARGF
Go
2
star
62

weave

Simple parallel SSH.
Ruby
2
star
63

hutil

Go net/http utilities.
Go
2
star
64

grb

go remote build
Go
2
star
65

kp

Keepass database manipulation
Go
2
star
66

guard-restarter

A Guard plugin to start something (like a server) and restart it when files change.
Ruby
2
star
67

window

Go package for maintaining a fixed window of a stream.
Go
2
star
68

ruby-complete

A ruby interface to bash command completion
Ruby
2
star
69

datef

Parse and print dates
Go
2
star
70

webtest

External copy of golang.org/x/website/internal/webtest
Go
2
star
71

jsonpath

Search for keys in nested JSON structures
Go
2
star
72

hmux

An HTTP muxer
Go
2
star
73

jquery-pixel-align

A jquery plugin to align SVGs to pixel boundaries.
HTML
2
star
74

git-heart

git ♥
1
star
75

usd

Go package for US Dollars
Go
1
star
76

cespare-brew-formulae

My personal brew formulae
Ruby
1
star
77

chunk

Read an arbitrary chunk from a file
Go
1
star
78

erebus

An easy-to-configure and flexible reverse routing proxy for development.
Go
1
star
79

githelpers

Helper tools for git
Go
1
star
80

fswatch

A small library to add a few features to github.com/fsnotify/fsnotify.
Go
1
star
81

dtd.vim

DTD syntax highlighting
Vim Script
1
star
82

wait

A Go package providing an extended version of sync.WaitGroup
Go
1
star
83

qs

Quickly take a screenshot and upload to Imgur (on Linux)
Shell
1
star
84

aoc2018

Advent of Code 2018
Go
1
star
85

ez-nginx-proxy

An nginx wrapper for simple reverse proxies in development.
Ruby
1
star
86

intelbacklight

An alternative for xbacklight that works with intel graphics
Go
1
star
87

pratbot

Go prat bot.
Go
1
star
88

srcstats

A source code analysis thing
Go
1
star
89

matasano

Solutions to the Matasano crypto challenges
Go
1
star
90

scrumcard

Planning Poker web app
JavaScript
1
star
91

cespare-snippets

Personal snipmate snippets
1
star
92

aoc2019

Advent of Code 2019
Go
1
star
93

rrproxy

A very simple routing reverse proxy built on Goliath.
Ruby
1
star
94

tlist

Minimal command-line TODO-list manager
Ruby
1
star