4

I try to script something similar to Removing files older then 1 month, but leave files created at the 1st day of the month.
The difference is that I need one file per month.

I am using the script with my build environment, which isn't executed daily caused by the scm polling.

I think the only way to achieve it is to use a find to get all "old" files and execute some checks against it.
First I would remove all files from the list, which has been kept as month file. Then I would remove one file as month file from the list and after it I would delete all files out of the list.

Did I missed a functionality of find or similar?

4
  • Is the creation date of the files in their filenames? file_2013-07-16.txt or something similar? Linux doesn't store the creation date of files (unless you're using some obscure file system), so unless you've recorded their creation dates in the filenames (or some filetype-specific metadata, or maybe a database), you'll have to do it based off of when they were last edited, rather than when they were created.
    – evilsoup
    Jul 16, 2013 at 10:54
  • Yes the creation date is contained by the filename. But we can use the modification time, too. The files are written to their location and are only read-only accessable.
    – CSchulz
    Jul 16, 2013 at 11:12
  • What is the pattern the files are named in? Please edit it into your question. Any answer that would rely on stuff contained in the filename would need to have the exact pattern in order to work.
    – evilsoup
    Jul 16, 2013 at 16:33
  • I have updated my answer extensively. If you had read the first version, you might want to have another look.
    – terdon
    Jul 17, 2013 at 1:36

1 Answer 1

4

Summary: The most complete solution is the last one, choose the one that suits you best depending on what exactly you want to do.


Actually, if all you want is to keep one file per month, you could just rename all files month. This will overwrite everything but one file per month. You have given no information about what type of file or where these files are. I am assuming that all files are in the same directory and there are no sub directories. If this is wrong, please update your question giving more details and I'll update my answer.

for file in *; do 
  month=$(date -d `stat -c %y $file | cut -f 1 -d ' ' ` +%B);
  mv $file $month;
done 

The script above will leave you with files named January, February etc. If you need to keep the original file names, you could try this:

for file in *; do 
 month=$(date -d `stat -c %y $file | cut -f 1 -d ' ' ` +%B); 
 rm *.$month; 
 mv $file $file.$month; 
done

This will leave you with files called <FILENAME>.<MONTH>, for example foo.January.


Both solutions above are very simplistic. A more general solution that will also work on files in sub directories and skips files that are less than a month old is:

find . -type f -mtime +29 | while IFS= read -r file; do 
 month=$(date -d `stat -c %y "$file" | cut -f 1 -d ' ' ` +%B); 
 find . -type f -name "*$month" -delete
 mv "$file" "$file.$month"; 
done

NOTES:

  • The solution above will look for files that are at least a month old in the current directory and all sub directories and will only keep one file per month. This means that if you have two files that were last modified in January, one of which is ./foo and the other baz/bar, only one will be kept, either ./foo.January or baz/bar.January.

  • This assumes that you don't have any files from last year that can be named foo.January. If you do, those will be deleted.


Finally, a slightly more complex one that will let you keep the names of the files unchanged and avoids the problems mentioned above:

tmp=`mktemp -u XXXX`;
find . -type f -mtime +29 | while IFS= read -r file; do 
  date=$(date -d `stat -c %y "$file" | cut -f 1 -d ' ' ` +%B%Y);
  foo=$tmp"_"$date; 
  find . -type f -name "*$foo" -delete
  mv "$file" "$file.$foo"; 
done
find . -type f -name "*$tmp*" | while IFS= read -r file; do 
  mv "$file" "${file%.*}";
done 

EXPLANATION:

This script uses mktemp -u XXXX (eg Xy12) to generate a random string which is then combined with the date (eg January2008) of the file. All files that are at least a month (>29 days, sorry February) old are then renamed to filename.Xy12_January2008. The second loop finds all files whose name contains the random string (Xy12) and removes their extension, so filename.Xy12_January2008 becomes filename again.

The final result is one file from each month of each year, with the original file name unchanged. The script can deal with file names with spaces and it will find files in the current directory and all sub directories.

You must log in to answer this question.

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