17

I want to automatically run a script whenever new files are copied into a particular directory. In other words, is there a way in Linux to "watch" a directory for changes and then run something in response to the change?

1

5 Answers 5

17

If you're lucky enough to be on a debian-based distribution, apt-get install dnotify. Other distributions probably have something similar - look for the dnotify name.

dnotify is a simple program based on Linux kernel 2.4.19+'s dnotify API. dnotify can execute a specified command each time the content of a specific directory changes. It is run from the command line and takes two arguments: one or more directories to monitor and a command to execute whenever a directory has changed. Options control what events to trigger on: when a file was read in the directory, when one was created, deleted and so on.

If you want to handle this within your own program, dnotify is also the API you want to use.

6
  • 4
    @MikeB, after Googling dnotify you suggested, I ended up using inotify instead, which apparently superceeds it. Thanks for pointing me the right direction. apt-get install inotify-tools
    – GeneQ
    Aug 5, 2009 at 2:21
  • 7
    @GeneQ, inotify has replaced dnotify.
    – David
    Aug 5, 2009 at 2:48
  • 1
    Likewise on Gentoo: emerge inotify-tools Aug 5, 2009 at 4:31
  • Yeah, I was going to say, kernel 2.4.19 is kind of ancient ;-) inotify is the way to go.
    – David Z
    Aug 5, 2009 at 5:50
  • Oh yeah... I forgot which was more recent :)
    – MikeyB
    Aug 5, 2009 at 18:27
12

You can run a script with the inotify-tools, something like this. It will watch the directory for changes in modified files, new files, and deleted files, then it will execute the script.

#!/bin/sh
while inotifywait -e modify -e create -e delete /home/me/code; do
    rsync [options] /home/me/code/ /media/nfs/code/ 
done
2
  • Just take into account that if the source directory changes while it is being copied, inotifywait will NOT see it and those changes will not be copied until further changes once the copy has finished. Probably not an issue though. May 16, 2013 at 9:30
  • How can you run this in daemon mode?
    – Chris F
    Dec 15, 2017 at 14:13
4

incron is basically what you want, I think. It uses inotify as the notification mechanism (which, as others have pointed out, supercedes dnotify), but doesn't require a script that continuously runs, using inotifywait or similar (although, obviously, the incron daemon is running all the time). System-wide 'crontabs' and user 'crontabs' are supported in a similar way to standard cron, but rather than specifying times as triggers, inotify events and files/directory names are used.

incron is packaged for many distributions, including Ubuntu and Debian, I believe.

0

There's a piece of software solely for this purpose, autoenv You might want to check it.

0

entr is the simplest and most composable file notification tool I have seen. Its use is optimized toward watching files rather than directories, but it can also solve your case.

To detect and act on the added file, combine it with other tools like e.g. make. entr doesn't send the name or anything like that, it simply runs what you told it to run.

To check for added files in a directory:

## entr exits with rc=0 when terminated
## rc=1 when watched files go away or don't exist to begin with
## rc=2 when new files arrive in watched directories
until echo /path/to/directory_to_watch | entr -d do_stuff
do sleep 1; done

If you want to also act when an existing file changes:

## Here's why it comes in handy that entr exits when new files are added --
## find gets re-run.
until find /path/to/directory_to_watch/ -path /path/to/directory_to_watch/* |
    entr -d do_stuff
do sleep 1; done

... and that's where the loop mechanism comes in handy, as the find expression will run again if a file is added.

If you want better error handling and want to make sure things only run once per added/removed file, things get a bit quirky, but for these simple cases it's brilliant.


EDIT: If you want to do this on a system level, something like incron, just add the script to your favorite process manager (like s6, runit, systemd or sysvinit and skip the loop:

#!/bin/bash
exec entr -d do_stuff < <(find /path/to/directory_to_watch/ -path /path/to/directory_to_watch/*)

The exec and the process substitution (<(...)) are important when running from a process manager, to handle signaling properly (i.e. to get the shell out of the way).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .