{"id":13393952,"url":"https://github.com/cespare/reflex","last_synced_at":"2026-02-27T01:20:31.213Z","repository":{"id":8561146,"uuid":"10187097","full_name":"cespare/reflex","owner":"cespare","description":"Run a command when files change","archived":false,"fork":false,"pushed_at":"2023-11-09T23:28:47.000Z","size":1133,"stargazers_count":3459,"open_issues_count":26,"forks_count":137,"subscribers_count":38,"default_branch":"main","last_synced_at":"2025-04-03T19:10:04.403Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cespare.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2013-05-21T03:26:29.000Z","updated_at":"2025-03-30T15:20:41.000Z","dependencies_parsed_at":"2024-03-15T08:16:02.565Z","dependency_job_id":null,"html_url":"https://github.com/cespare/reflex","commit_stats":{"total_commits":116,"total_committers":10,"mean_commits":11.6,"dds":"0.18103448275862066","last_synced_commit":"01cf5215c3517716ab0fd1ae87ec6093057efab9"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cespare%2Freflex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cespare%2Freflex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cespare%2Freflex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cespare%2Freflex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cespare","download_url":"https://codeload.github.com/cespare/reflex/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248338027,"owners_count":21087151,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T17:01:03.500Z","updated_at":"2026-02-27T01:20:31.202Z","avatar_url":"https://github.com/cespare.png","language":"Go","readme":"# Reflex\n\nReflex is a small tool to watch a directory and rerun a command when certain\nfiles change. It's great for automatically running compile/lint/test tasks and\nfor reloading your application when the code changes.\n\n## A simple example\n\n    # Rerun make whenever a .c file changes\n    reflex -r '\\.c$' make\n\n## Installation\n\nYou can download binaries from the\n[Releases page](https://github.com/cespare/reflex/releases).\n\nTo compile from source, you'll need Go 1.13+ installed.\n\nIf you have Go 1.16 or later, you can download and install the latest module\nversion directly with\n\n    go install github.com/cespare/reflex@latest\n\nReflex is only tested on Linux and macOS.\n\n## Usage\n\nThe following is given by running `reflex -h`:\n\n```\nUsage: reflex [OPTIONS] [COMMAND]\n\nCOMMAND is any command you'd like to run. Any instance of {} will be replaced\nwith the filename of the changed file. (The symbol may be changed with the\n--substitute flag.)\n\nOPTIONS are given below:\n      --all=false:\n            Include normally ignored files (VCS and editor special files).\n  -c, --config=\"\":\n            A configuration file that describes how to run reflex\n            (or '-' to read the configuration from stdin).\n  -d, --decoration=\"plain\":\n            How to decorate command output. Choices: none, plain, fancy.\n  -g, --glob=[]:\n            A shell glob expression to match filenames. (May be repeated.)\n  -G, --inverse-glob=[]:\n            A shell glob expression to exclude matching filenames.\n            (May be repeated.)\n  -R, --inverse-regex=[]:\n            A regular expression to exclude matching filenames.\n            (May be repeated.)\n      --only-dirs=false:\n            Only match directories (not files).\n      --only-files=false:\n            Only match files (not directories).\n  -r, --regex=[]:\n            A regular expression to match filenames. (May be repeated.)\n  -e, --sequential=false:\n            Don't run multiple commands at the same time.\n  -t, --shutdown-timeout=500ms:\n            Allow services this long to shut down.\n  -s, --start-service=false:\n            Indicates that the command is a long-running process to be\n            restarted on matching changes.\n      --substitute=\"{}\":\n            The substitution symbol that is replaced with the filename\n            in a command.\n  -v, --verbose=false:\n            Verbose mode: print out more information about what reflex is doing.\n\nExamples:\n\n    # Print each .txt file if it changes\n    $ reflex -r '\\.txt$' echo {}\n\n    # Run 'make' if any of the .c files in this directory change:\n    $ reflex -g '*.c' make\n\n    # Build and run a server; rebuild and restart when .java files change:\n    $ reflex -r '\\.java$' -s -- sh -c 'make \u0026\u0026 java bin/Server'\n```\n\n### Overview\n\nReflex watches file changes in the current working directory and re-runs the\ncommand that you specify. The flags change what changes cause the command to be\nrerun and other behavior.\n\n### Patterns\n\nYou can specify files to match using either shell glob patterns (`-g`) or\nregular expressions (`-r`). If you don't specify either, reflex will run your\ncommand after any file changes. (Reflex ignores some common editor and version\ncontrol files; see Ignored files, below.)\n\nYou can specify inverse matches by using the `--inverse-glob` (`-G`) and\n`--inverse-regex` (`-R`) flags.\n\nIf you specify multiple globs/regexes (e.g. `-r foo -r bar -R baz -G x/*/y`),\nonly files that match all patterns and none of the inverse patterns are\nselected.\n\nThe shell glob syntax is described\n[here](http://golang.org/pkg/path/filepath/#Match), while the regular expression\nsyntax is described [here](https://code.google.com/p/re2/wiki/Syntax).\n\nThe path that is matched against the glob or regular expression does not have a\nleading `./`. For example, if there is a file `./foobar.txt` that changes, then\nit will be matched by the regular expression `^foobar`. If the path is a\ndirectory, it has a trailing `/`.\n\n### --start-service\n\nThe `--start-service` flag (short version: `-s`) inverts the behavior of command\nrunning: it runs the command when reflex starts and kills/restarts it each time\nfiles change. This is expected to be used with an indefinitely-running command,\nsuch as a server. You can use this flag to relaunch the server when the code is\nchanged.\n\n### Substitution\n\nReflex provides a way for you to determine, inside your command, what file\nchanged. This is via a substitution symbol. The default is `{}`. Every instance\nof the substitution symbol inside your command is replaced by the filename.\n\nAs a simple example, suppose you're writing Coffeescript and you wish to compile\nthe CS files to Javascript when they change. You can do this with:\n\n    reflex -r '\\.coffee$' -- coffee -c {}\n\nIn case you need to use `{}` for something else in your command, you can change\nthe substitution symbol with the `--substitute` flag.\n\n### Configuration file\n\nWhat if you want to run many watches at once? For example, when writing web\napplications I often want to rebuild/rerun the server when my code changes, but\nalso build SCSS and Coffeescript when those change as well. Instead of running\nmultiple reflex instances, which is cumbersome (and inefficient), you can give\nreflex a configuration file.\n\nThe configuration file syntax is simple: each line is a command, and each\ncommand is composed of flags and arguments -- just like calling reflex but\nwithout the initial `reflex`. Lines that start with `#` are ignored. Commands\ncan span multiple lines if they're \\\\-continued, or include multi-line strings.\nHere's an example:\n\n    # Rebuild SCSS when it changes\n    -r '\\.scss$' -- \\\n       sh -c 'sass {} `basename {} .scss`.css'\n\n    # Restart server when ruby code changes\n    -sr '\\.rb$' -- \\\n        ./bin/run_server.sh\n\nIf you want to change the configuration file and have reflex reload it on the\nfly, you can run reflex inside reflex:\n\n    reflex -s -g reflex.conf -- reflex -c reflex.conf\n\nThis tells reflex to run another reflex process as a service that's restarted\nwhenever `reflex.conf` changes.\n\n### --sequential\n\nWhen using a config file to run multiple simultaneous commands, reflex will run\nthem at the same time (if appropriate). That is, a particular command can only\nbe run once a previous run of that command finishes, but two different commands\nmay run at the same time. This is usually what you want (for speed).\n\nAs a concrete example, consider this config file:\n\n    -- sh -c 'for i in `seq 1 5`; do sleep 0.1; echo first; done'\n    -- sh -c 'for i in `seq 1 5`; do sleep 0.1; echo second; done'\n\nWhen this runs, you'll see something like this:\n\n    [01] second\n    [00] first\n    [01] second\n    [00] first\n    [00] first\n    [01] second\n    [01] second\n    [00] first\n    [01] second\n    [00] first\n\nNote that the output is interleaved. (Reflex does ensure that each line of\noutput is not interleaved with a different line.) If, for some reason, you need\nto ensure that your commands don't run at the same time, you can do this with\nthe `--sequential` (`-e`) flag. Then the output would look like (for example):\n\n    [01] second\n    [01] second\n    [01] second\n    [01] second\n    [01] second\n    [00] first\n    [00] first\n    [00] first\n    [00] first\n    [00] first\n\n### Decoration\n\nBy default, each line of output from your command is prefixed with something\nlike `[00]`, which is simply an id that reflex assigns to each command. You can\nuse `--decoration` (`-d`) to change this output: `--decoration=none` will print\nthe output as is; `--decoration=fancy` will color each line differently\ndepending on which command it is, making it easier to distinguish the output.\n\n### Ignored files\n\nReflex ignores a variety of version control and editor metadata files by\ndefault. If you wish for these to be included, you can provide reflex with the\n`--all` flag.\n\nYou can see a list of regular expressions that match the files that reflex\nignores by default\n[here](https://github.com/cespare/reflex/blob/master/defaultexclude.go#L5).\n\n## Notes and Tips\n\nIf you don't use `-r` or `-g`, reflex will match every file.\n\nReflex only considers file creation and modification changes. It does not report\nattribute changes nor deletions.\n\nFor ignoring directories, it's easiest to use a regular expression: `-R '^dir/'`.\n\nMany regex and glob characters are interpreted specially by various shells.\nYou'll generally want to minimize this effect by putting the regex and glob\npatterns in single quotes.\n\nIf your command has options, you'll probably need to use `--` to separate the\nreflex flags from your command flags. For example: `reflex -r '.*\\.txt' -- ls\n-l`.\n\nIf you're going to use shell things, you need to invoke a shell as a parent\nprocess:\n\n    reflex -- sh -c 'sleep 1 \u0026\u0026 echo {}'\n\nIf your command is running with sudo, you'll need a passwordless sudo, because\nyou cannot enter your password in through reflex.\n\nIt's not difficult to accidentally make an infinite loop with certain commands.\nFor example, consider this command: `reflex -r '\\.txt' cp {} {}.bak`. If\n`foo.txt` changes, then this will create `foo.txt.bak`, `foo.txt.bak.bak`, and\nso forth, because the regex `\\.txt` matches each file. Reflex doesn't have any\nkind of infinite loop detection, so be careful with commands like `cp`.\n\nThe restart behavior works as follows: if your program is still running, reflex\nsends it SIGINT; after 1 second if it's still alive, it gets SIGKILL. The new\nprocess won't be started up until the old process is dead.\n\n### Batching\n\nPart of what reflex does is apply some heuristics to batch together file\nchanges. There are many reasons that files change on disk, and these changes\nfrequently come in large bursts. For instance, when you save a file in your\neditor, it probably makes a tempfile and then copies it over the target, leading\nto several different changes. Reflex hides this from you by batching some\nchanges together.\n\nOne thing to note, though, is that the the batching is a little different\ndepending on whether or not you have a substitution symbol in your command. If\nyou do not, then updates for different files that all match your pattern can be\nbatched together in a single update that only causes your command to be run\nonce.\n\nIf you are using a substitution symbol, however, each unique matching file will\nbe batched separately.\n\n### Argument list splitting\n\nWhen you give reflex a command from the commandline (i.e., not in a config\nfile), that command is split into pieces by whatever shell you happen to be\nusing. When reflex parses the config file, however, it must do that splitting\nitself. For this purpose, it uses [this library](https://github.com/kballard/go-shellquote)\nwhich attempts to match `sh`'s argument splitting rules.\n\nThis difference can lead to slightly different behavior when running commands\nfrom a config file. If you're confused, it can help to use `--verbose` (`-v`)\nwhich will print out each command as interpreted by reflex.\n\n### Open file limits\n\nReflex currently must hold an open file descriptor for every directory it's\nwatching, recursively. If you run reflex at the top of a big directory tree, you\ncan easily run into file descriptor limits. You might see an error like this:\n\n    open some/path: too many open files\n\nThere are several things you can do to get around this problem.\n\n1. Run reflex in the most specific directory possible. Don't run\n   `reflex -g path/to/project/*.c ...` from `$HOME`; instead run reflex in\n   `path/to/project`.\n2. Ignore large subdirectories. Reflex already ignores, for instance, `.git/`.\n   If you have other large subdirectories, you can ignore those yourself:\n   `reflex -R '^third_party/' ...` ignores everything under `third_party/` in\n   your project directory.\n3. Raise the fd limit using `ulimit` or some other tool. On some systems, this\n   might default to a restrictively small value like 256.\n\nSee [issue #6](https://github.com/cespare/reflex/issues/6) for some more\nbackground on this issue.\n\n## The competition\n\n* https://github.com/guard/guard\n* https://github.com/alexch/rerun\n* https://github.com/mynyml/watchr\n* https://github.com/eaburns/Watch\n* https://github.com/alloy/kicker\n* https://github.com/eradman/entr\n\n### Why you should use reflex instead\n\n* Reflex has no dependencies. No need to install Ruby or anything like that.\n* Reflex uses an appropriate file watching mechanism to watch for changes\n  efficiently on your platform.\n* Reflex gives your command the name of the file that changed.\n* No DSL to learn -- just give it a shell command.\n* No plugins.\n* Not tied to any language, framework, workflow, or editor.\n\n## Authors\n\n* Benedikt Böhm ([hollow](https://github.com/hollow))\n* Caleb Spare ([cespare](https://github.com/cespare))\n* Jim Kalafut ([kalafut](https://github.com/kalafut))\n* PJ Eby ([pjeby](https://github.com/pjeby))\n* Rich Liebling ([rliebling](https://github.com/rliebling))\n* Seth W. Klein ([sethwklein](https://github.com/sethwklein))\n* Vincent Vanackere ([vanackere](https://github.com/vanackere))\n","funding_links":[],"categories":["开源类库","Go","File System Events Monitoring","Open source library","Tools","\u003ca name=\"file-watch\"\u003e\u003c/a\u003eFile watching for changes","File system"],"sub_categories":["文件/存储","Snippets Manager","Files/Storage","Go"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcespare%2Freflex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcespare%2Freflex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcespare%2Freflex/lists"}