remove oldest files
Solution 1
If you want to loop over files, never use ls
*. tl;dr There are lots of situations where you'd end up deleting the wrong file, or even all files.
That said, unfortunately this is a tricky thing to do right in Bash. There's a working answer over at a duplicate question my even older find_date_sorted
which you can use with small modifications:
counter=0
while IFS= read -r -d '' -u 9
do
let ++counter
if [[ counter -gt 3 ]]
then
path="${REPLY#* }" # Remove the modification time
echo -e "$path" # Test
# rm -v -- "$path" # Uncomment when you're sure it works
fi
done 9< <(find . -mindepth 1 -type f -printf '%TY-%Tm-%TdT%TH:%TM:%TS %p\0' | sort -rz) # Find and sort by date, newest first
* No offense guys - I also used ls
before. But it really isn't safe.
Edit: New find_date_sorted
with unit tests.
Solution 2
By far the easiest method is to use zsh and its glob qualifiers: Om
to sort by decreasing age (i.e. oldest first) and [1,3]
to retain only the first three matches.
rm ./*(Om[1,3])
See also How do I filter a glob in zsh for more examples.
And do heed l0b0's advice: your code will break horribly if you have file names that contain shell special characters.
Solution 3
To delete all but the 3 newest files using a zsh glob, you can use Om
(capital O) to sort the files from oldest to newest and a subscript to grab the files you want.
rm ./*(Om[1,-4])
# | |||| ` stop at the 4th to the last file (leaving out the 3 newest)
# | |||` start with first file (oldest in this case)
# | ||` subscript to pick one or a range of files
# | |` look at modified time
# | ` sort in descending order
# ` start by looking at all files
Other examples:
# delete oldest file (both do the same thing)
rm ./*(Om[1])
rm ./*(om[-1])
# delete oldest two files
rm ./*(Om[1,2])
# delete everything but the oldest file
rm ./*(om[1,-2])
Solution 4
You can use the following function to get the newest file in a directory:
newest_in()
{
local newest=$1
for f;do [[ $f -nt $newest ]] && newest="$f"; done;
printf '%s\n' "$newest"
}
Give it a different set of files by exculding the newest file after each iteration.
Tip: If you hold the initial set of files in an array called "${files[@]}", then save the index of the newest file found, and unset 'files[index]'
before the next iteration.
Usage: newest_in [set of files/directories]
Sample output:
[rany$] newest_in ./*
foo.txt
[rany$]
Related videos on Youtube
d3vil0
Updated on September 18, 2022Comments
-
d3vil0 over 1 year
I`m trying to delete old files from directory and leave only 3 newest files.
cd /home/user1/test while [ `ls -lAR | grep ^- | wc -l` < 3 ] ; do rm `ls -t1 /home/user/test | tail -1` echo " - - - " done
something is wrong with conditional statement.
-
rahmu about 12 yearsI second the part about not parsing ls. Also the script is neat but I think it could be done in a one liner, let me check ...
-
l0b0 about 12 yearsWell, you can always compact Bash code into a one-liner, but the issue is whether it'll be readable :)
-
Sorpigal about 12 yearsIf I may mercilessly steal an idea from @Peter.O, try
((++counter>3))
as your test. It's nicely succinct. As for oneliners: If brevity is a concern wrap the code in a function, then don't worry about it. -
l0b0 about 12 years@Sorpigal Neat shortcut
-
Marian-Daniel Craciunescu about 12 yearsWoa woa hold the train, is this really a complete solution in 1 line?
-
Rany Albeg Wein about 11 yearsBoth will fail if one or more of the file names contain a '$\n'.
-
Rany Albeg Wein about 11 yearsThis one will fail for two reasons - Parsing
ls
output and usingfind
andxargs
together in the wrong way. If you combinefind
andxargs
I recommend you to usefind
s-print0
and accordinglyxargs
s-O
options. Read about Using Find for more information about the subject. You might also want to take a look and read about why Parsing ls is very bad. -
Rany Albeg Wein about 11 yearsls is a tool for interactively looking at file information. Its output is formatted for humans and will cause bugs in scripts. Use globs or find instead. Understand why: mywiki.wooledge.org/ParsingLs