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:
-
Image files in standard background sizes/ratios should be moved to
/home/daf/media/bg
-
tar files containing openbox or gtk themes should be extracted to
/home/daf/.themes
-
Image files not matching standard background sizes/ratios should be
moved to /home/daf/media
-
Torrent files should be scp'd to my server, where rtorrent will
automatically start downloading them
Writing simple handling scripts, fsniper can detect these files when
they are written and those scripts are called.
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)
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.
Mime-Types
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.
Handlers
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
-
0 : the handler handled this file.
-
2 : the handler will handle this event, but not right now. Retry again later.
-
other: this handler can not handle this file.
Delays
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.
-
delay_time : the amount of time, in seconds, to wait before trying the handler
again. If not specified, it defaults to 300, which is 5 minutes.
-
delay_repeats : the number of repeats it should try before giving up. This can
be 0, meaning infinite retries.
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.