Thursday, March 08, 2007


My colleague was asking me whether it is possible to "tail -2000 bigfile.txt > bigfile.txt" without creating an empty file. As you may already know if you literally run the above command, you will get an empty file. So the question is, is it possible to do that with one-liner. Also, is it possible to do it safely without using intermediate temporary file ?

IMO, you may be able to copy some bytes over and overwrite the original file instead of getting an empty file. It will depend on how much intermediate bytes the stream can store in the buffer. This example uses subshell to proof the point, but final line count will definitely going to be different from what you expect. (cat bigfile.txt;sleep 1) | dd of=bigfile.txt. You cannot achieve this by using file redirection because the shell will open a new file before the execution of the tail command.

If we really want a failsafe one-liner, I will have to use temp file

n=2000;f=bigile.txt;t=.$f.$$;tail -$n $f > $t && mv -f $t $f

If you need to do this very often, it is wise to put in an extact effort to either crank out a shell script or a function. Below is a shell function that you can run it like this keep 2000 bigfile.txt. As you can see, I actually ensure all the arguments are ok to avoid the shell function from throwing execption. Also, if the line count of a file is less than the required lines to be kept, I simply do not proceed with the commands. I used a lot of short-circuit syntax to avoid all the if-then statement.

 [ $# -eq 2 -a $1 -eq $1 -a -r $2 ] >/dev/null 2>&1 && \
 ( n=$1; f=$2; t=.$f.$$; [ $n -le `wc -l $f | awk '{print $1}'` ] && tail -$n $f > $t && mv -f $t $f )
Just run "keep 2000 bigfile.txt"

Happy one-liner!

Labels: ,


Post a Comment

<< Home