The find command is very powerful when it comes to searching for files, yet it’s missing a way to find files older than a certain date. There is the -mtime parameter that allows you to find files modified in the last x days but sometimes you need to specify dates, like “all files from 2008 and 2009”. This can be done using the “newer”-parameter and “touch”. It’s probably not the most elegant solution but it works fine and is easy to understand.
Basically, you just use touch to create special files that specify the timeframe. For example to find files older than 2009 you would create a file as follows:
touch -d'2009-01-01 00:00:00' end
This creates a file “end” with the specified timestamp that then can be used with find:
find /var/backup/ ! -newer end
As said before, you can also use this to specify a timeframe by creating a second file …
touch -d'2006-01-01 00:00:00' start
… and using it with the find command:
find /var/backup/ ! -newer end -newer start
This would match all files from 2006, 2007 and 2008.
Of course the date can be modified to match pretty much any timeframe necessary.
Additionally you may filter certain filetypes that you want or don’t want to be matched. The following command:
find /var/backup/ ! -newer end -newer start -iname '*.jpg'
will find every Jpeg file in the specified timeframe. iname works as case insensitive, whereas the corresponding -name is case sensitive.
It’s also possible to filter multiple names with the -o parameter. An example would be as follows:
find /var/backup/ ! -newer end \( -iname '*.mkv' -o -iname '*.avi' \)
This finds all files in /var/backup, that are not newer than the “end”-file and end to either .mkv or .avi (or .AVI or .mKv …).
There are plenty more options to define what files you want to be found but these examples should suffice to show the basic usage.
Now that we’ve matched our files it’s time to do something with them. That’s where -exec enters the stage.
Exec allows you to execute a single statement for every matched file. Of course it could also invoke a script but to keep it simple I’m only going to use commands like rm, ls or zip.
A good start is always to list the files that have been matched using ls.
find /var/backup/ ! -newer end -newer start -exec ls -la '{}' \;
The curly braces will be replaced with the filename that has been matched and the command will be executed. This is pretty easy and straight-forward. You can also use it with rm or bzip or whatever you want.
However, if you’re dealing with a LOT of files this will run very slow, since the command is invoked for every single file. This might not be a problem when compressing files, since the compression is taking most of the time anyway but if you’re just listing or removing files, the overhead from spawning a new process for every execution becomes substantial.
To speed things up there’s another syntax with a “+” at the end of the line:
find /var/backup/ ! -newer end -newer start -exec ls -la '{}' +
This appends the filenames to the end of the line until the maximum length has been reached. That way one process of rm may delete several files, reducing the overhead and speeding up the overall process. The difference is probably insiginificant when processsing 10 files but when dealing with several hundred or thousand matches you’ll definitely notice a difference.
An alternative to this syntax is using xargs. Xargs takes everything from standard input and splits it up into smaller lists so the maximum number of arguments for a single command is not reached. Example:
find /var/backup/ ! -newer end -print0 | xargs -0 mv {} /var/backup/archive
As with exec, the curly braces define where the argument will be inserted. The parameter print0 prints every result and additionally replaces whitespaces with zeroes so filenames with spaces will work just fine. The corresponding -0 in xarg allows it to expect the right format.
This is just a simple alternative to exec, you probably won’t notice any difference to it when using the “+”-syntax.