I store some data in files which follow this naming convention:


How do I look for the ones with date in file name < now - 1 month and delete them?

Files may have changed since they were created, so searching according to last modification date is not good.

What I'm doing now, is filter-ing them in python:

prefix = '/interesting/data/filename-'

import commands
names = commands.getoutput('ls {0}*'.format(prefix)).splitlines()

from datetime import datetime, timedelta

all_files = map(lambda name: {
    'name': name,
    'date': datetime.strptime(name, '{0}%Y-%m-%d-%H-%M'.format(prefix))
}, names)

month = datetime.now() - timedelta(days = 30)
to_delete = filter(lambda item: item['date'] < month, all_files)

import os
from operator import itemgetter
map(os.remove, map(itemgetter('name'), to_delete))

Is there a (oneliner) bash solution for this?

2 Answers 2


Can you use -ctime with find? For a file, ctime indicates the last time the file metadata has changed (file creation, rename, chmod, chown, chgrp, etc). For most logfiles, creation date and ctime would be the same.

  • According to man find: -ctime n File’s status was last changed n*24 hours ago. That only returns filenames from that exact date, but in my case there may be files older than than, and I can't tell how old they are, so I really need a ` < ` or ` <= ` comparison. Jan 13, 2011 at 8:33
  • You are right. man find says: Numeric arguments can be specified as: +n (for greater than n), -n (for less than n), n (for exactly n). And it works as desired. Thanks! Jan 14, 2011 at 9:40

Presuming GNU date and GNU find you can do it this way

#!/usr/bin/env bash


# create a file whose timestamp is "one month ago"
touch "$one_month_ago" -t $(date -d "-1 month" +%Y%m%d%I%M.%S)

while read -r file ; do
        # strip the prefix, leaving the suffix
        datestr=$(tail -c $(( ${#file} - ${#prefix} + 1 )) <<<"$file")

        # cut the date and time out of the suffix
        date=$(cut -d- -f1-3 <<<"$datestr")
        time=$(cut -d- -f 4- <<<"$datestr" | tr - :)

        # create a reference file whose timestamp matches the string from $file
        touch "$ref" -t $(date -d "$date $time" +%Y%m%d%I%M.%S)

        # ask find whether the reference file is not neewer (aka "is older") 
        # than one month ago
        find "$ref" -not -newer "$one_month_ago" > "$results" &&
                # results from find?
                [ -s "$results" ] &&
                # then rm the corresponding file
                echo rm -f -- "$file"

done < <(find -path "$prefix"'*')

# clean up
rm -f "$ref" "$one_month_ago" "$results"

But it's not exactly a oneliner.

Since this is under tested and somewhat dangerous I have included an echo prefix to the rm command, so you'll need to remove that once you verify that the results are correct.

One weakness here is in the initial selection of files. -path "$prefix"'*' presumes absolute paths and will break otherwise; a more intelligent selection is probably better, even if it's a simple shell glob (i.e., replace the while loop with for file in "$prefix*" ; do ... done. I didn't do this because I don't know whether or not such a glob expansion would overflow the maximum command length.

  • 1
    Interesting, it actually does the compares. It will be a bit slower because of all the touch files. The find . ! -newer' can much more simply be replaced by the [ $FILE1 -ot $FILE2 ]` flag, at least in bash. Jan 14, 2011 at 16:22
  • Given all of the GNUisms present, including the explicit invocation of bash and the use of non-portable find syntax, I don't think anyone could object to the use of -ot. I would have used this comparison if I had thought about it, but instead I recycled a solution I've used before (when I had GNU find on Windows, but no sh).
    – phogg
    Jan 14, 2011 at 17:10
  • As it turns out you can use -newerXY where Y is t to specify a timestamp directly (for GNU find it's GNU date -d format) to avoid the need to create an intermediate file. This will be a little faster, as Rich mentions.
    – phogg
    Jan 26, 2011 at 0:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.