Sign up ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I want a quick and simple way to execute a command whenever a file changes. I want something very simple, something I will leave running on a terminal and close it whenever I'm finished working with that file.

Currently, I'm using this:

while read; do ./ ; done

And then I need to go to that terminal and press Enter, whenever I save that file on my editor. What I want is something like this:

while sleep_until_file_has_changed ; do ./ ; done

Or any other solution as easy as that.

BTW: I'm using Vim, and I know I can add an autocommand to run something on BufWrite, but this is not the kind of solution I want now.

Update: I want something simple, discardable if possible. What's more, I want something to run in a terminal because I want to see the program output (I want to see error messages).

About the answers: Thanks for all your answers! All of them are very good, and each one takes a very different approach from the others. Since I need to accept only one, I'm accepting the one that I've actually used (it was simple, quick and easy-to-remember), even though I know it is not the most elegant.

share|improve this question

22 Answers 22

up vote 182 down vote accepted

Simple, using inotifywait (install your distribution's inotify-tools package):

while inotifywait -e close_write; do ./; done

This has a big limitation: if some program replaces with a different file, rather than writing to the existing myfile, inotifywait will die. Most editors work that way.

To overcome this limitation, use inotifywait on the directory:

while true; do
  change=$(inotifywait -e close_write,moved_to,create .)
  change=${change#./ * }
  if [ "$change" = "" ]; then ./; fi
share|improve this answer
I've encapsulated all of this (with quite a few bash tricks) in a simple-to-use script, available at: – Denilson Sá Aug 30 '10 at 0:57
while derivation.tex ; do latexmk -pdf derivation.tex ; done is fantastic. Thank you. – Rhys Ulerich Dec 15 '11 at 16:49
inotifywait does not play well with temporary files. If you save a file with vim (:w) you'll get 2 CREATE and DELETE signals, as well as 2 MOVE signals.… – puk May 10 '12 at 22:30
inotifywait -e delete_self seems to work well for me. – Kos Oct 1 '13 at 11:29
It's simple but has two important issues: Events may be missed (all events in the loop) and initialization of inotifywait is done each time which makes this solution slower for large recursive folders. – Wernight Apr 29 '14 at 13:00

A little more on the programming side, but you want something like inotify. There are implementations in many languages, such as jnotify and pyinotify.

This library allows you to monitor single files or entire directories, and returns events when an action is discovered. The information returned includes the file name, the action (create, modify, rename, delete) and the file path, among other useful information.

share|improve this answer

Solution using Vim:

:au BufWritePost :silent !./

But I don't want this solution because it's kinda annoying to type, it's a bit hard to remember what to type, exactly, and it's a bit difficult to undo its effects (need to run :au! BufWritePost In addition, this solution blocks Vim until the command has finished executing.

I've added this solution here just for completeness, as it might help other people.

To display the program output (and completely disrupt your editting flow, as the output will write over your editor for a few seconds, until you press Enter), remove the :silent command.

share|improve this answer

Have a look at incron. It's similar to cron, but uses inotify events instead of time.

share|improve this answer

If your program generates some sort of log/output, you can create a Makefile with a rule for that log/output that depends on your script and do something like

while true; do make -s my_target; sleep 1; done

Alternately, you can create a phony target and have the rule for it both call your script and touch the phony target (while still depending on your script).

share|improve this answer
while sleep 1 ; do something ; done is slightly better than while true ; do something ; sleep 1 ; done. At least it stops easily when pressing Ctrl+C. – Denilson Sá Aug 28 '10 at 4:59
@StevenLu: no, the sleep is not a busy wait. The problem is that if the sleep is in the body, Control-C will kill the sleep and the loop will start over. Power usage of starting the loop over is insignificant. Try it yourself in a terminal. You need to hold Control-C for it to work, if you have sleep in the body. – Janus Troelsen Sep 19 '12 at 11:25

Here's a simple shell Bourne shell script that:

  1. Takes two arguments: the file to be monitored and a command (with arguments, if necessary)
  2. Copies the file you are monitoring to the /tmp directory
  3. Checks every two seconds to see if the file you are monitoring is newer than the copy
  4. If it's newer it overwrites the copy with the newer original and executes the command
  5. Cleans up after itself when you press Ctr-C

    tmpf="`mktemp /tmp/onchange.XXXXX`"  
    cp "$f" "$tmpf"  
    trap "rm $tmpf; exit 1" 2  
    while : ; do  
        if [ "$f" -nt "$tmpf" ]; then  
            cp "$f" "$tmpf"  
        sleep 2  

This works on FreeBSD. The only portability issue I can think of is if some other Unix doesn't have the mktemp(1) command, but in that case you can just hard code the temp file name.

share|improve this answer
Polling is the only portable way, but most systems have a file change notification mechanism (inotify on Linux, kqueue on FreeBSD, ...). You have a severe quoting problem when you do $cmd, but fortunately that's easily fixable: ditch the cmd variable and execute "$@". Your script is not suitable for monitoring a large file, but that could be fixed by replacing cp by touch -r (you only need the date, not the contents). Portability-wise, the -nt test requires bash, ksh or zsh. – Gilles Aug 27 '10 at 22:22

I wrote a Python program to do exactly this called when-changed.

Usage is simple:

when-changed FILE COMMAND...

Or to watch multiple files:

when-changed FILE [FILE ...] -c COMMAND

FILE can be a directory. Watch recursively with -r. Use %f to pass the filename to the command.

share|improve this answer
@ysangkok yes it does, in the latest version of the code :) – joh Oct 11 '12 at 16:35
Now available from "pip install when-changed". Still works nicely. Thanks. – A. L. Flanagan Jan 8 at 18:37

If you happen to have npm installed, nodemon is probably the easiest way to get started, especially on OS X, which apparently doesn't have inotify tools. It supports running a command when a folder changes.

share|improve this answer
However, it only watches .js and .coffee files. – cormacrelf Jul 20 '12 at 10:27
The current version seems to support any command, for example: nodemon -x "bundle exec rspec" spec/models/model_spec.rb -w app/models -w spec/models – kek Apr 10 '13 at 15:15
I wish I had more info, but osx does have a method to track changes, fsevents – hobs Oct 7 '14 at 4:34
On OS X you can also use Launch Daemons with a WatchPaths key as shown in my link. – Adam Johns Nov 16 '14 at 23:48

Under Linux:

man watch

watch -n 2 your_command_to_run

Will run the command every 2 seconds.

If your command takes more than 2 seconds to run, watch will wait until it's done before doing it again.

share|improve this answer
What happens when the command takes longer than two seconds to run? – thirtythreeforty Feb 24 at 6:53

For those of you who are looking for a FreeBSD solution, here is the port:

share|improve this answer

How about this script? It uses the stat command to get the access time of a file and runs a command whenever there is a change in the access time (whenever file is accessed).


### Set initial time of file
LTIME=`stat -c %Z /path/to/the/file.txt`

while true    
   ATIME=`stat -c %Z /path/to/the/file.txt`

   if [[ "$ATIME" != "$LTIME" ]]
       echo "RUN COMMNAD"
   sleep 5
share|improve this answer
Bonus points for simplicity and using basic build-in unix tools :) – CJBrew Oct 18 '13 at 10:54

entr ( provides a more friendly interface to inotify (and also supports *BSD & Mac OS X).

It makes it very easy to specify multiple files to watch (limited only by ulimit -n), takes the hassle out of dealing with files being replaced, and requires less bash syntax:

$ find . -name '*.py' | entr ./

I've been using it on my entire project source tree to run the unit tests for the code I'm currently modifying, and it's been a huge boost to my workflow already.

As of May 2014 it's still in active development, with new flags like -c (clear the screen between runs) and -d (exit when a new file is added to a monitored directory).

entr can now also be found in in Debian Jessie/Sid, although it's not got the -d flag there yet. (Building from the author's repo was pain-free anyway.)

share|improve this answer
that works great and seems to be actively maintained. – nikola Mar 10 '14 at 14:28
available in aur – Victor Häggqvist Jul 1 at 16:33

Another solution with NodeJs, fsmonitor :

  1. Install

    sudo npm install -g fsmonitor
  2. From command line (example, monitor logs and "retail" if one log file change)

    fsmonitor -s -p '+*.log' sh -c "clear; tail -q *.log"
share|improve this answer

I like the simplicity of while inotifywait ...; do ...; done however it has two issues:

  • File changes happening during the do ...; will be missed
  • Slow when using in recursive mode

Therefor I made a helper script that uses inotifywait without those limitations: inotifyexec

I suggest you put this script in your path, like in ~/bin/. Usage is described by just running the command.

Example: inotifyexec "echo test" -r .

share|improve this answer

Look into Guard, in particular with this plugin:

You can set it up to watch any number of patterns in your project's directory, and execute commands when changes occur. Good chance even that there's a plugin available for that what you're trying to do in the first place.

share|improve this answer

if you have nodemon installed, then you can do this:

nodemon -w <watch directory> -x "<shell command>" -e ".html"

In my case I edit html locally and ship it to my remote server when a file changes.

nodemon -w <watch directory> -x "scp filename" -e ".html"
share|improve this answer

For people who find this by Googling for changes to a particular file, the answer is much simpler (inspired by Gilles's answer).

If you want to do something after a particular file has been written to, here's how:

while true; do
  inotifywait -e modify /path/to/file
  # Do something *after* a write occurs, e.g. copy the file
  /bin/cp /path/to/file /new/path

Save this as, for example, and put the .sh file into the /etc/init.d/ folder to have it run on startup.

share|improve this answer

Improved upon Gilles's answer.

This version runs inotifywait once and monitors for events (.e.g.: modifiy) thereafter. Such that inotifywait doesn't need to be re-executed upon every event encountered.

It's quick and fast!(even when monitoring large directory recursively)

inotifywait --quiet --monitor --event modify FILE | while read; do
    # trim the trailing space from inotifywait output
    REPLY=${REPLY% }
    filename=${REPLY%% *}
    # do whatever you want with the $filename
share|improve this answer
I rolled these fixes up into an anwer of my own: search this page for 'rerun2' – Jonathan Hartley Sep 9 at 21:50

A oneliner answer that I'm using to keep track on a file change:

$ while true ; do NX=`stat -c %Z file` ; [[ $BF != $NX ]] && date >> ~/tmp/fchg && BF=$NX || sleep 2 ; done

You don't need to initialize BF if you know that the first date is the starting time.

This is simple and portable. There is another answer based on the same strategy using a script here. Take a look also.

Usage: I'm using this to debug and keep an eye on ~/.kde/share/config/plasma-desktop-appletsrc; that for some unknown reason keeps loosing my SwitchTabsOnHover=false

share|improve this answer

I wrote a Python program to do exactly this, called rerun.

UPDATE: This answer is not remotely as good as my other answer, search this page for 'rerun2'.

Install with:

pip install rerun

and usage is very simple:

rerun "COMMAND"

The command is expected as a single arg, not a sequence of space-separated args. Hence quote it as shown, which reduces any extra escaping you'd have to add. Just type the command as you would have typed it at the command line, but surrounded by quotes.

By default it watches all files in or under the current directory, skipping things like known source control dirs, .git, .svn, etc.

Optional flags include '-i NAME' which ignores changes to named files or directories. This can be given multiple times.

Since it's a Python script, it needs to run the command as a sub-process, and we use a new instance of the user's current shell to interpret 'COMMAND' and decide what process to actually run. However, if your command contains shell aliases and the like which are defined in .bashrc, these will not be loaded by the subshell. To fix this, you can give rerun a '-I' flag, to use interactive (aka 'login') subshells. This is slower and more error-prone than starting a regular shell, because it has to source your .bashrc.

I use it with Python 3, but last I checked rerun still worked with Python 2.

Double-edged sword is that it uses polling instead of inotify. On the upside, this means it works on every OS. Plus, it's better than some other solutions shown here in terms of only running the given command once for a bunch of filesystem changes, not once per modified file, while at the same time it does run the command a second time if any files change again while command is running.

On the downside, polling means that there is a 0.0 to 1.0 second latency, and of course it's slow to monitor extremely large directories. Having said that, I've never encountered a project large enough that this is even noticeable so long as you use '-i' to ignore big things like your virtualenv and node_modules.

Hmmm. rerun has been indispensible to me for years - I basically use it eight hours every day for running tests, rebuilding dot files as I edit them, etc. But now I come to type this up here, it's clear that I need to switch to a solution that uses inotify (I no longer use Windows or OSX.) and is written in Bash (so it works with aliases without any extra fiddling.)

share|improve this answer

rerun2 (on github) is a 10-line Bash script of the form:

#!/usr/bin/env bash

function execute() {
    echo "$@"
    eval "$@"

execute "$@"

inotifywait --quiet --recursive --monitor --event modify --format "%w%f" . \
| while read change; do
    execute "$@"

Save the github version as 'rerun' on your PATH, and invoke it using:


It runs COMMAND every time there's a filesystem modify event within your current directory (recursive.)

Things one might like about it:

  • It uses inotify, so is more responsive than polling. Fabulous for running sub-millisecond unit tests, or rendering graphviz dot files, every time you hit 'save'.
  • Because it's so fast, you don't have to bother telling it to ignore large subdirs (like node_modules) just for performance reasons.
  • It's extra super responsive, because it only calls inotifywait once, on startup, instead of running it, and incurring the expensive hit of establishing watches, on every iteration.
  • It's just 12 lines of Bash
  • Because it's Bash, it interprets commands you pass it exactly as if you had typed them at a Bash prompt. (Presumably this is less cool if you use another shell.)
  • It doesn't lose events that happen while COMMAND is executing, unlike most of the other inotify solutions on this page.
  • On the first event, it enters a 'dead period' for 0.15 seconds, during which other events are ignored, before COMMAND is run exactly once. This is so that the flurry of events caused by the create-write-move dance which Vi or Emacs does when saving a buffer don't cause multiple laborious executions of a possibly slow-running test suite. Any events which then occur while COMMAND is executing are not ignored - they will cause a second dead period and subsequent execution.

Things one might dislike about it:

  • It uses inotify, so won't work outside of Linuxland.
  • Because it uses inotify, it will barf on trying to watch directories containing more files than the max number of user inotify watches. By default, this seems to be set to around 5,000 to 8,000 on different machines I use, but is easy to increase. See
  • It fails to execute commands containing Bash aliases. I could swear that this used to work. In principle, because this is Bash, not executing COMMAND in a subshell, I'd expect this to work. I'd love to hear If anyone knows why it doesn't. Many of the other solutions on this page can't execute such commands either.
  • Personally I wish I was able to hit a key in the terminal it's running in to manually cause an extra execution of COMMAND. Could I add this somehow, simply? A concurrently running 'while read -n1' loop which also calls execute?
  • Right now I've coded it to clear the terminal and print the executed COMMAND on each iteration. Some folks might like to add command-line flags to turn things like this off, etc. But this would increase size and complexity many-fold.

This is a refinement of @cychoi's anwer.

share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.