If you can be 100% sure your file names never contain spaces or other whitespace, you can do the simple, fragile and bad way:
ls -t | tail -n+3 | xargs rm
-t
tells ls
to sort by time, and tail -n+3
means "start printing after the second line of input", so it will print all except the last two files. Finally, you pass these to xargs rm
to delete them.
A better, safe and robust approach that would work for arbitrary file names is something like this:
stat --printf '%Y %n\0' * | sort -zrnk1,1 | cut -zd' ' -f 2- | tail -zn+3 | xargs -0 rm
The stat --printf '%Y %n\0' *
will print the modification date (in seconds since the epoch) along with the file name for each file (or directory) in the current directory, ending each line with a NUL byte (\0
) instead of a newline. This allows us to deal even with file names with newlines in their names.
Next, the sort -zrnk1,1
sorts its NUL-terminated (-z
) input, in reverse (-r
) numerical (-n
) order, according to the first field only (-k1,1
). This is passed through cut -zd' ' -f 2-
to remove the first space-separated field, which removes the timestamp. To illustrate:
$ ls -l *
-rw-r--r-- 1 terdon terdon 0 Oct 11 15:00 'a bad file name'
-rw-r--r-- 1 terdon terdon 0 Oct 11 15:00 'another'$'\n''bad file name'
-rw-r--r-- 1 terdon terdon 0 Oct 12 15:00 file.1
-rw-r--r-- 1 terdon terdon 0 Oct 12 15:00 file.2
-rw-r--r-- 1 terdon terdon 0 Oct 12 15:00 file.3
-rw-r--r-- 1 terdon terdon 0 Oct 12 15:00 file.4
-rw-r--r-- 1 terdon terdon 0 Oct 12 15:00 file.5
Now, running the commands above gives:
$ stat --printf '%Y %n\0' *
1665489608 a bad file name1665489610 another
bad file name1665576030 file.11665576031 file.21665576032 file.31665576033 file.41665576034 file.5
$ stat --printf '%Y %n\0' * | sort -zrnk1,1
1665576034 file.51665576033 file.41665576032 file.31665576031 file.21665576030 file.11665489610 another
bad file name1665489608 a bad file name
$ stat --printf '%Y %n\0' * | sort -zrnk1,1 | cut -zd' ' -f 2-
file.5file.4file.3file.2file.1another
bad file namea bad file name
All of this is finally passed to tail -zn+3 | xargs -0 rm
to delete the files as in the first example.