0

I have a folder that contains files. I want to write a script that will delete all the files except for the last modified file of each day. For example: If I have this folder:

Uriels-MacBook-Pro-3:demo urielfrankel$ ls -la
total 304
drwxr-xr-x  36 urielfrankel  staff  1224 Apr 10 17:35 .
drwxr-xr-x   3 urielfrankel  staff   102 Apr 10 17:45 ..
-rw-r--r--   1 urielfrankel  staff  2483 Apr 10 17:55 ApiClientAsyncTask.java
-rw-r--r--   1 urielfrankel  staff  5258 Apr 11 12:35 BaseDemoActivity.java
-rw-r--r--   1 urielfrankel  staff  2157 Apr 11 13:35 CreateEmptyFileActivity.java
-rw-r--r--   1 urielfrankel  staff  3753 Apr 9 16:35 CreateFileActivity.java
-rw-r--r--   1 urielfrankel  staff  2756 Apr 9 17:35 CreateFileInAppFolderActivity.java
-rw-r--r--   1 urielfrankel  staff  3376 Apr 8 10:35 CreateFileInFolderActivity.java
-rw-r--r--   1 urielfrankel  staff  3302 Apr 8 11:35 CreateFileWithCreatorActivity.java
-rw-r--r--   1 urielfrankel  staff  1864 Apr 8 12:35 CreateFolderActivity.java

After executing the script, it will look like this:

Uriels-MacBook-Pro-3:demo urielfrankel$ ls -la
total 105
drwxr-xr-x  36 urielfrankel  staff  1224 Apr 10 17:35 .
drwxr-xr-x   3 urielfrankel  staff   102 Apr 10 17:45 ..
-rw-r--r--   1 urielfrankel  staff  2483 Apr 10 17:55 ApiClientAsyncTask.java
-rw-r--r--   1 urielfrankel  staff  2157 Apr 11 13:35 CreateEmptyFileActivity.java
-rw-r--r--   1 urielfrankel  staff  2756 Apr 9 17:35 CreateFileInAppFolderActivity.java
-rw-r--r--   1 urielfrankel  staff  1864 Apr 8 12:35 CreateFolderActivity.java
Uriel Frankel
  • 14,304
  • 8
  • 47
  • 69

4 Answers4

1
for day in $(ls -lap --time-style full-iso | grep -v / | awk '{print $6}' | sort | uniq ); do ls -lap --time-style full-iso | grep -v / | grep "$day" | awk '{print $7"\t"$9}' | sort -r | sed '1d' | awk '{print $2}' | xargs rm -f; done

This will list only files in the current directory and print their modification date with full-iso format (down to nanoseconds), find the unique days and delete all but the last modified file of each day without asking (rm -f, so beware).

There are some flaws with this line:

  • It will always only leave the last modified file of that day. If more than one have the exact same modification time, then only one will be kept.
  • It cannot handle files with spaces
hellerpop
  • 509
  • 4
  • 7
1

Borrowing some part of the find command from Gunstick's answer. Here is a solution that is truly space safe and delete the files. Use with cautions. Better duplicate your files to another directory and run from there first.

find . -type f -printf "%TY%Tm%Td|%Ts|%f\n" |\
 sort -t'|' -k1n -k2nr |\
 awk -F'|' '(a[$1]){print $3} {a[$1]++}' |\
xargs -I{} rm -f {}

This code was tested on Ubuntu Linux.

An I don't know why you want to do this. I would consider a git-based solution myself.

biocyberman
  • 5,675
  • 8
  • 38
  • 50
  • space safe, but not pipe safe. Create yourself a file with `echo a>"test|file"` and have fun. And you missed the `-maxdepth 1`to stay in the current directory. – Gunstick May 18 '16 at 22:19
  • @Gunstick : Tell me, which one is more commonly found in filenames, spaces or pipes? `-maxdepth 1 ` is upto user preference. – biocyberman May 23 '16 at 08:20
  • I searched for '|' on my filesystem... xchat logfiles, saved youtube videos, pdf of hotel tickets from HRS, titanium backup files from my android. Not a lot, but they exist :-) – Gunstick Jun 20 '16 at 13:17
0

this lists the files, it won't delete them
This one should be space safe but not special character safe.

find . -maxdepth 1 -type f -printf "%TY%Tm%Td %Ts %f\n" | 
  sort -k1n -k2nr|
  awk 'a[$1]++{    # increment day's counter, if it was 0: print line
                sub("^[0-9]* *[0-9]* *","")   # remove timestamp stuff
                print
              }'

%TY gives the year of the timestamp
%Tm the month
%Td the day
%Ts is the unix timestamp for correct order per day

then the list is sorted by day and reverse by timestamp and awk prints out the first filename of each day. Not thoroughly tested for the first file.

Gunstick
  • 993
  • 10
  • 15
  • The `awk` part is unnecessarily complicated. And it produces wrong result. Do like this: `sort -k1n -k2nr | awk '(!a[$1]){print $3} {a[$1]++}' ` Essentially print only the first line of every date after sorting. – biocyberman May 18 '16 at 12:54
  • I now changed my answer to add some of your sort/awk ideas. I still kept using space as separator so the sub() has to stay. – Gunstick May 18 '16 at 22:15
0

Thanks everyone, finally I did it myself with the help of my co-workers.

#!/bin/bash
for day in $(find ./ -type f -mtime +0 -print0 | xargs -0 stat -l -t "%F %T %z" | awk '{print $6}' | sort | uniq ); do stat -l -t "%F %T %z" * | grep -v / | grep "$day" | awk '{print $7"\t"$9}' | sort -r | sed '1d' | awk '{print $2}' | xargs rm -f; done
Uriel Frankel
  • 14,304
  • 8
  • 47
  • 69