views:

2425

answers:

7

Is there a simple way, in a pretty standard UNIX environment with bash, to run a command to delete all but the most recent X files from a directory?

To give a bit more of a concrete example, imagine some cron job writing out a file (say, a log file or a tar-ed up backup) to a directory every hour. I'd like a way to have another cron job running which would remove the oldest files in that directory until there are less than, say, 5.

And just to be clear, there's only one file present, it should never be deleted.

A: 

Remove all but 5 (or whatever number) of the most recent files in a directory.

rm `ls -t | awk 'NR>5'`
Espo
+10  A: 
(ls -t|head -n 5;ls)|sort|uniq -u|xargs rm
thelsdj
This command will not correctly handle files with spaces in the names.
tylerl
+2  A: 

If the filenames don't have spaces, this will work:

ls -C1 -t| awk 'NR>5'|xargs rm

If the filenames do have spaces, something like

ls -C1 -t| awk 'NR>5'|sed -e "s/^/rm '" -e "s/$/'/'|sh

Basic logic:

  • get a listing of the files in time order, one column
  • get all but the first 5 (n=5 for this example)
  • first version: send those to rm
  • second version: gen a script that will remove them properly
Mark Harrison
+2  A: 

All these answers fail when there are directories in the current directory. Here's something that works:

find . -maxdepth 1 -type f | xargs -x ls -t | awk 'NR>5' | xargs -L1 rm

This:

a) works when there are directories in the current directory

b) tries to remove each file even if the previous one couldn't be removed (perms/etc)

c) fails safe when the number of files in the current directory is excessive and xargs would normally screw you over (the -x)

d) doesn't cater for spaces in filenames (perhaps you're using the wrong OS?)

+4  A: 
find . -maxdepth 1 -type f -printf '%T@ %p\0' | sort -r -z -n | awk 'BEGIN { RS="\0"; ORS="\0"; FS="" } NR > 5 { sub("^[0-9]*(.[0-9]*)? ", ""); print }' | xargs -0 rm -f

Requires GNU find for -printf, and GNU sort for -z, and GNU awk for "\0", and GNU xargs for -0, but handles files with embedded newlines or spaces.

wnoise
+3  A: 

Ignoring newlines is ignoring security and good coding. wnoise had the only good answer. Here is a variation on his that puts the filenames in an array $x

while read -rd ''; do 
    x+=("${REPLY#* }"); 
done < <(find . -maxdepth 1 -printf '%T@ %p\0' | sort -r -z -n )
Ian Kelling
A: 

Running on Debian (assume its the same on other distros I get: rm: cannot remove directory `..'

which is quite annoying..

Anyway I tweaked the above and also added grep to the command. In my case I have 6 backup files in a directory e.g. file1.tar file2.tar file3.tar etc and I want to delete only the oldest file (remove the first file in my case)

The script I ran to delete the oldest file was:

ls -C1 -t| grep file | awk 'NR>5'|xargs rm

This (as above) deletes the first of my files e.g. file1.tar this also leaves be with file2 file3 file4 file5 and file6

Peter Mark Ellis