Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have a number of log files in the form:

log.2014-02-19-10_24_22

I.e. log.YYYY-MM-DD-H24_MI_SS

The date that's part of the log file's name is when the log file was first created. So at any given moment in time I can have the following log files in my directory:

log.2014-02-19-10_18_54
log.2014-02-19-10_21_20
log.2014-02-19-10_23_11
etc.

Now I have a script that's invoked by a cronjob and that deletes "old" log files:

$ cat delete-old-rotated-logs 
#!/usr/bin/env bash
find /home/foo -maxdepth 1 -iname log\* -type f -mmin +1800 -exec rm {} \;

The problem I am facing is that sometimes the process that's logging has crashed so the "latest" log file also becomes "old" after some time (since no process is writing on it) and gets deleted, thus losing me the trace. How can I re-write the delete-old-rotated-logs script so that it deletes old files except the last one (or the last N) ? For the ordering one can use both the filename itself or the modification timestamp (more robust).

share|improve this question

2 Answers 2

up vote 1 down vote accepted
find /home/foo -maxdepth 1 -iname log\* -type f -mmin +1800 |
  sort | head -n -1 | xargs rm

Or if you want to use mtime instead of the filename:

find /home/foo -maxdepth 1 -iname log\* -type f -mmin +1800 -exec ls -t {} + |
  tail -n +2 | xargs rm

From @Stephane's comments, a more robust approach would be to do:

IFS=$'\n'
set -f
rm $(
  find /home/foo -maxdepth 1 -iname log\* ! -name $'*\n*' -type f -mmin +1800 |
  sort | head -n -1 )

Or for POSIX shell (still requires GNU tools):

IFS='
'
ex_newline='*
*'
set -f
rm $(
  find /home/foo -maxdepth 1 -iname log\* ! -name "$ex_newline" -type f -mmin +1800 |
  sort | head -n -1 )

A single (robust) pipeline can be used with a recent version of GNU sed/sort (and GNU find as with all of the above):

find /home/foo -maxdepth 1 -iname log\* -type f -mmin +1800 -print0 |
  sort -z | sed -z '$d' | xargs -0 rm
share|improve this answer
    
For xargs, newline is not the only problematic character. space, tab, single quote, double quote and backslash are as well. –  Stéphane Chazelas Feb 19 at 12:18
    
Your ls -t-based one would not work properly if find runs more than one ls (if the list of files is big enough that it wants to split it in several ls invocations). –  Stéphane Chazelas Feb 19 at 12:20
    
Note that none of -maxdepth, -iname, -mmin, head -n -1 are POSIX anyway. –  Stéphane Chazelas Feb 19 at 12:20
    
Yeah, I know about that one, I was just showing the shell syntax. The xargs issue is new to me though, thank you. –  Graeme Feb 19 at 12:27

bash/ksh93/zsh:

n=5        # Maximum number of files to keep
cd /home/foo || exit
files=(log.*)
((${#files[@]} <= n)) ||
  rm -f -- "${files[@]:0:${#files[@]}-n}"

With zsh, it can be simplified to:

n=5
rm -f /home/foo/log.*(N[1,-n-1])

And if you want to only consider the files that are over 30 hours old:

n=5
rm -f /home/foo/log.*(Nmh+30[1,-n-1])

POSIXly:

n=5
cd /home/foo || exit
set -- log.*
i=$#
while [ "$i" -gt "$n" ]; do
  set -- "$@" "$1"
  shift
  i=$(($i - 1))
done
shift "$i"
rm -f -- "$@"
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.