0

Consider the following folder structure

dir
    sandbox1
        mywebsite file 
        ...
    sandbox2
        mywebsite file
        ...

I have thousands of these sandbox directory created by my peers.

Since we are running out of inode, we decide to delete sandbox directory that has content not modified for 20 days.

eg.

dir
    sandbox1 (modified 23 days ago)
        mywebsite file (modified 22 days ago)
        ... (modified 24 days ago)
    sandbox2 (modified 23 days ago)
        mywebsite file (modified 19 days ago)
        ...

In this case sandbox1 will be deleted since it has not been modified for 20 days, and its content has not been modified for 20 days

Sandbox2 will not be deleted, since it has content that is modified 19 days ago

I know

find /dir/ -maxdepth 1 -mtime +n 

finds all directory modified at least n days, but the content inside each directory is not reflected.

Is there a way to find all directory such that the directory and its content have not been modified for n days?

Any help would be appreciated.

  • Why do you have -maxdepth specified to 1 if you want it to scan all subdirecories/files and not just /dir/? – Owen Hines Nov 3 '16 at 18:54
  • Is this homework? Your question as written is a bit confusing. Please confirm if what you intend is to check each directory modified between 20 and 20 days ago and if they contain no files modified in the past 3 days you wish to delete that directory. If this is your intent please edit your post and make that clear, if not please edit your post and clarify exactly what you want. It would be useful if you would review askubuntu.com/help/how-to-ask – Elder Geek Nov 3 '16 at 20:54
  • @ElderGeek , this is not homework, I am running out of inodes for my sandbox build, I want to delete any sandbox build directory that have not being touch for 20 days (eg. any content under this directory that has not being modified for at least 20 days, this means if one of the file inside is modified for 19 days or 18 days then we should not delete the sandbox directory). The command find /dir/ -maxdepth 1 -mtime +n only find sandbox root directory within /dir/ that is not modify for 20 days, but it doesn't check the file inside the sandbox root directory. – user445670 Nov 3 '16 at 21:21
  • Please edit that information into your question as comments can be deleted for a number of reasons. Thank you for helping us help you! – Elder Geek Nov 3 '16 at 21:23
  • @ElderGeek fair point, I will edit the additional information, thanks for pointing that out. – user445670 Nov 3 '16 at 21:25
0

If file modification times are critical for you then you need to look at file modification times and not the modification time of their parent directories. The latter only change when the structure of the directory changes (i. e. a file was created, moved/renamed or unlinked). Changes to file content are not reflected in the time stamp of the parent directory.

Therefore we can find all the recently modified (less than 20×24 h ago) files in a directory tree with:

find /some/path -type f -mtime -20

We can restrict the output to show only unique directory names:

find /some/path -type f -mtime -20 -printf '%h\n' | uniq

Find all directories without recently modified files

If we need to find all the directories without recently modified deep entries it gets trickier since we need to compute the inverted set, that is the set difference of the set of all directories inside the tree and the previously computed set. We can easily use the -printf action to partition the output of find to at least list all the data we need:

find /some/path -mindepth 1 \( -type d -printf '+%p\n' \) -o \( -type f -mtime -20 -printf '-%h\n' \) | uniq

Unfortunately, set operations aren't something that can be easily done in a shell script, so I wrote a Python program that operates on the output of the previous find command:

#!/usr/bin/env python3
import sys, os.path
from itertools import filterfalse   

def parent_dir_generator( path ):
    while path:
        yield path
        path = os.path.dirname(path)

all_dirs = list()
keep_dirs = set()
keep_dir_parents = set()

for line in filter(bool, map(lambda s: s.rstrip('\n'), sys.stdin)):
    path = line[1:]
    if path.startswith('./'):
        path = path[2:]

    if line.startswith('+'):
        all_dirs.append(path)
    elif line.startswith('-'):
        keep_dirs.add(path)
        keep_dir_parents.update(parent_dir_generator(path))

diff_dirs = filterfalse(
    lambda path: any(map(keep_dirs.__contains__, parent_dir_generator(path))),
    filterfalse(keep_dir_parents.__contains__, all_dirs))

print(*diff_dirs, sep='\n')

Assuming the previous program is at ~/tree-difference.py we can use it like this:

find /some/path -mindepth 1 -depth \( -type d -printf '+%p\n' \) -o \( -type f -mtime -20 -printf '-%h\n' \) | python3 ~/tree-difference.py

Verify the result

You probably want to verify that you (or I) didn't make a mistake that accidentally deletes recently modified files. Fortunately we can use a variation of the original find command to inspect all the directories returned by tree-difference.py. It lists all recently modified files in them, so an empty output means everything went according to plan. This can take quite a while if we have many files.

The following command takes its input from the output of tree-difference.py (with a pipe or intermediate file):

xargs -rd '\n' -I{} -- find {} -mindepth 1 -type f -mtime -20

Delete the found directories

This one is simple. The input is the output of tree-difference.py.

xargs -rd '\n' -- rm -rf --

If rm complains about non-existing directories that's because you forgot the -depth option in the find command serving as the input of tree-difference.py.

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.