Delete files in a directory older than 7 days, but always leave at least 20 files intact

5,755

Solution 1

So we want to get the (regular) files that have mtime more than 7 days ago AND are not within the 20 newest files.

One way to do that is to construct a list of files along with their modification times and a flag indicating whether the file is mtime +7 or not. We can then sort the list by mtime, and take all but the 20 most recent1. Finally, iterate over the resulting list and delete only those that meet the age threshold:

find ./recentpicturesdirectory -type f \( -mtime +7 -printf 'Y\t' -o -printf 'N\t' \) -printf '%A@\t%p\0' |
  sort -zk2,2 | head -zn -20 | while read -r -d '' flag _ file; do \
    case "$flag" in 
      'Y') echo rm "$file" 
           ;; 
        *) echo "skipping $file (too new)"
           ;; 
    esac
  done

By using \0 termination and adding the -z flag to the sort and head (or tail) commands, we can handle any legal filenames without choking on whitespace.

The echo is added for testing purposes; remove it once you're certain that the desired files are selected for deletion.

1 If we sort in ascending order of mtime (i.e. old to new), we can use head -n -20 to select all but the last 20; alternatively we could sort in descending order of mtime (new to old) and use tail -n +21


If you have zsh, then you can do it all with glob qualifiers, I think:

rm ./recentpicturesdirectory/**/*(.^om[1,20]^m+7)

where

  • **/* matches recursively (equivalent of bash globstar)
  • (.) match regular files only
  • om[1,20] lists results in ascending order of mtime, and select the first 20
  • m+7 match only files with mtime > 7 days
  • ^ invert everything that follows

so the logic is

regular files NOT (in 20 most recent by mtime OR NOT mtime +7 days)

which (by application of de Morgan's rules) is equivalent to

regular files (NOT in 20 most recent by mtime) AND (mtime +7 days)

Please do a trial run first though e.g.

print -rl ./recentpicturesdirectory/**/*(.^om[1,20]^m+7)

Solution 2

Here is a simple script to do this:

#!/bin/bash

IMGSPATH="/path/to/somewhere"

# count of all files within your directory
count=$(find $IMGSPATH -type f | wc -l)

# remove the ones older than of 7 days
while [ "$count" -gt "20" ]
do 
 find $IMGSPATH -type f -mtime +7 -print -delete -quit
 count=$((--count))
done
  1. First we count all files within your desired directory.
  2. While the number of files within that directory is greater than of "20", then do:
    • Find the first file older than of "7" days.
    • Remove it
    • Decrease the count of available files
    • Do "#2" again ...

To test it:

mkdir /tmp/lab
cd /lab
touch {1..40}
touch -d "10 days ago" {1..20}

Save the script and run it for /tmp/lab now files 1 to 20 should be deleted, run:

touch -d "10 days ago" {21..35}

run the script again, nothing will get removed cause you don't have more than of 20 files whatever they're old or not.

Share:
5,755

Related videos on Youtube

Dominik Meyer
Author by

Dominik Meyer

Updated on September 18, 2022

Comments

  • Dominik Meyer
    Dominik Meyer over 1 year

    Beginner here: I want to sync pictures to a folder but want them to be recent. That's why I want them to be deleted if they are older then 7 days.

    Current code for it:

    find ./recentpicturesdirectory -mtime -7 -type f -delete 
    

    Now I need an exception because there should always be a minimum of 20 pictures in the folder.

    I did some research but can't figure it out. Maybe use something else then the find command?

    First sad try (told you I'm a beginner)

    SIZE=find recentpictesfolder -type f | wc -l
    
    find ./recentpicturesdirectory -mtime -7 -type f \(-iname ".*" ! **if files part of -> .. i dont know ...**$SIZE ) -delete