For those not wanting to make assumptions on the names of the files:
With zsh
:
#! /bin/zsh -
keep=5000
rm -f /mnt/md0/capture/DCN/*.pcap(D.om[$((keep+1)),-1])
That's using zsh
globbing qualifiers:
D
: includes hidden files (Dot files).
.
: only regular files (like find
's -type f
)
om
: reverse order on the age (based on modification time)
[$((keep+1)),-1]
: only include the 5001st to the last.
(it may fail if the list of files to remove is very big, in which case you may want to use zargs
to split it, or enable zsh
's builtin rm
with zmodload zsh/files
).
With relatively recent versions of GNU tools:
cd /mnt/md0/capture/DCN/ &&
find . -maxdepth 1 -name '*.pcap' -type f -printf '%T@@%p\0' |
sort -zrn | sed -z "s/[^@]*@//;1,$keep d" | xargs -r0 rm -f
(assuming GNU sed 4.2.2 or above (2012) for -z
, GNU sort
1.14 or above (1996) for -z
)
find
builds a NUL delimited list of filenames with a Unix timestamp prepended (like 1390682991.0859627500@./file
) which is sorted by sort
. sed
removes the timestamp and prints only from the 5001st record. That's passed as arguments to rm
using xargs -r0
.
or (with any version of GNU tools):
cd /mnt/md0/capture/DCN/ &&
find . -maxdepth 1 -name '*.pcap' -type f -printf '%T@@%p\0' |
tr '\0\n' '\n\0' | sort -rn | tail -n "+$(($keep+1))" |
cut -d @ -f2- | tr '\0\n' '\n\0' | xargs -r0 rm -f
Same, except that we're using cut
to remove the timestamp and tail
to select the lines starting from 5001. Because GNU cut
and tail
don't support a -z
to work on NUL delimited records, we use tr
to swap the newline and NUL characters before and after feeding the data to them.
With GNU ls
(4.0 (1998) or above), and bash
:
shopt -s dotglob
cd /mnt/md0/capture/DCN/ &&
eval "files=($(ls -dt --quoting-style=shell-always -- *.pcap))" &&
rm -f -- "${files[@]:$keep}"
(that also may fail if the list of file is big. Also note that it may include non-regular pcap files (no -type f
)).
Standardly/POSIXly/portably, that's a lot trickier:
cd /mnt/md0/capture/DCN/ &&
ls -dt ./.pcap ./.*.pcap ./*.pcap | awk -v keep="$keep" '
function process() {
if (++n > keep) {
gsub(/[ \t\n"\\'\'']/,"\\\\&", file)
print file
file = ""
}
}
/\// {
if (NR > 1) process()
file=$0
next
}
{file = file "\n" $0}
END {if (NR > 0) process()}' | xargs rm -f
(again, you may reach the limit of the number of arguments, and it does not check for regular files).
The tricky bit there is to handle the filenames with newline characters. Above, we're passing ./*
to ls
which means /
will be included once for each file name, and we use that in awk
to identify on which line each filename starts, then we know which newline character (in addition to all the other ones special to xargs
) to escape for xargs
.
tai
should betail
. Also if any of the filenames has newline in themls
intotail
is not going to work