fsniper is a utility that waits for a file to be changed, then executes a command on that file.


fsniper was inspired by the need to watch a directory for certain types of files and move them to their appropriate location. I wanted a utility that could intelligently figure out what type of file I was putting in that directory, and move it to where it needed to go. For example:

Writing simple handling scripts, fsniper can detect these files when they are written and those scripts are called.

Technical Details

fsniper uses inotify to watch for when a file is closed after being written to. This means utilities such as touch will cause the event to trigger.

fsniper can watch any number of directories for any number of pattern matches on the files. (technically, it can only watch up to the limit of inotify, typically 8192 dirs)

Example Configuration File

Let's jump into an example configuration file.

watch {

    ~/tmp/s {
        image/* {
            handler = echo

        *.extension {
            handler = ./foo1 a %% b
            handler = mv %% arc-%%.txt

        /.*regex.*/ {
            handler = ./foo2 a %% b

        */* {
            handler = echo 2


To start defining directories to watch, you must have a watch block. Each block underneath a watch block is a directory that fsniper monitors for files that are closed.

In this configuration, we are watching only one dir, ~/tmp/s. Underneath that directory block, we are watching for 4 different patterns of files. The first pattern, image/*, looks for files that match a mime-type pattern. Any image file will go into this block. The handler is echo, a standard system command that writes to stdout. fsniper looks for "%%" in the handler line. The name of the file that was just closed is substituted in for the %%. If no %% is found in the handler line, it appends it on the end.

The second pattern, *.extension, matches a shell glob for any file with the .extension extension. This pattern has two handlers. ./foo1 is a custom script we write. If that script returns a return code that is not 0 or 2, it moves on to the next handler. See the Handler Return Codes section.

The third pattern, /.regex./, matches the file name against a regular expression pattern. PCRE support must be compiled in for this pattern to work.

The fourth pattern, /, matches any file (by mime-type wildcards). You could also use *, which would be a file glob.


This section details all the available configuration options.

Matching Patterns

There are three types of patterns that fsniper can match against.


fsniper can match mime-types, like text/plain, image/png, etc. You can use the file utility to figure out the mime-type, use file -ib to get the mime-type that fsniper sees.

You can also have wildcards on each side of the mime-type, so image/* would match all images, etc.

Shell Globs

The most common pattern would be shell globs, like .txt or .html. This matches any shell like file pattern.

Regular Expressions

fsniper can be compiled with PCRE support to allow more complicated regular expression matching. Regex patterns are enclosed in forward slashes. Consult the PCRE documentation for the flavor of regular expressions supported. Remember, the entire filename (including path) is matched against the regex.


The handler scripts do not always have to be custom written. You can use the %% replacement to do some script-like behavior. For example, this handler line:

handler = tar cvz archive-%%.tar.gz %%

This will create a tar/gz file of the files that match the file pattern.

Often, custom scripts are easier if they need to perform some complicated tests, or indicate that they will delay.

Handler Search Paths

Since fsniper uses the shell to execute handlers, the shell's PATH environment variable is respected to find the handlers. fsniper automatically prepends ~/.config/fsniper/scripts into PATH, so it is common to place custom scripts in that directory.

Handler Return Codes


It is possible that a handler can handle a file, but not at this moment. A common example would be that the handler is supposed to scp a file to a remote server, but that server is currently not available. The handler script returns a 2 to indicate that it can handle it, just not right now. fsniper detects this return code and sleeps for a period of time, then retries the handler.

There are two parameters that control delay handling. These parameters are placed in the configuration file on the top level, aka the same level as the watch block.

fsniper Files

By default, fsniper tries to load its configuration from ~/.config/sniper/config. You can override this by specifying a configuration file on the command line.

fsniper keeps a log file in ~/.config/sniper/log. You can use tail -f to monitor fsniper's output.

Command Line Options

                Prints this help text.
                Prints version information.
                Run as a daemon.
                Turns on debug text.
                Sync mode (for debugging).
                Log to stdout alongside the usual log file.

Special Note: Firefox

(to be written)