tags:

views:

357

answers:

4

I'm trying to manage my log file size using a cron script. I basically want to delete all but the last 2000 lines of the log file every night. I'm trying to run this command, but it seems to be emptying the entire file instead of doing what I want:

tail -2000 logfile.txt > logfile.txt

Does anyone know why this isn't working and/or how to accomplish what I want? Thanks!

+6  A: 

You are overwriting the file before tail even starts to read it. The shell processes the > redirect operator, by clearing out the file first. Then it runs tail which has no data to read.

You can solve this by using a temporary file:

tail -2000 logfile.txt >logfile.tmp
mv logfile.tmp logfile.txt
Greg Hewgill
`> logfile.txt` - means create new file
Ivan Nevostruev
yes but if the filename is the same as the input, it would overwrite it
Patonza
This only works if the process writing to the logfile closes its file descriptor. Typical, but not always true.
NVRAM
A: 

Greg Hewgill is right, logfile.txt is being truncated before tail can work on it.

try:

tail -2000 logfile.txt > logfile2.txt; rm -f logfile.txt; mv logfile2.txt logfile.txt
Patonza
In this case `rm` is unnecessary because `mv` will unlink the destination file during the rename.
Greg Hewgill
Right... I was just being redundant :P
Patonza
This only works if the process writing to the logfile closes its file descriptor. Typical, but not always true.
NVRAM
This is not correct, NVRAM. It works even if the process does not close its FD. It does truncate the file without error. But the logging process may not become aware of the file substitution and logging may cease to work until the [new] file is reopened.
Patonza
+3  A: 

Rather than doing this with your own cron file you might want to look into using logrotate for a more robust solution. You can rotate the logs, control how long to keep them around, mail them, compress old logs, and run scripts afterwards if you want to.

http://linuxcommand.org/man%5Fpages/logrotate8.html or type man logrotate from the command line

Create a new logrotate file in your /etc/logrotate.d/ directory. Here's an example:

/var/logs/myapp/logfile.txt {
  # keep 5 files
  rotate 5
  # rotate once the file is bigger than 2k
  size 2k
  # don't error if the file isn't there
  missingok
  # old files are compressed
  compress
  # set ownership and permissions on the new log file
  create 0640 myuser myuser
}
mmrobins
+4  A: 

There is a problem with the accepted solution if the process keeps the log file open; you basically need to reuse the i-node. Mmrobins' response is good, logrotate should do the right thing.

To use tail, you can do something (similar to Pantonza's & Greg's idea), but retain the original file by truncating the original file in-place:

tail -2000 logfile.txt >logfile.tmp
cat logfile.tmp > logfile.txt
rm logfile.tmp

To avoid a temp file, you could read into a variable, then stuff it back:

bash -c 'X=$(<tail -2000 logfile.txt);echo "$x">logfile.txt'

In all cases, there's the possibility of a race condition between your truncation and the process appending to the file. Not sure if logrotate handles that, none of the tail solutions here do.

NVRAM