Deleting all lines except last 500

Hi All,

I want to write a script which first check the line counts of a file if its more than 500 it deletes rest except the last 500..

I tried sed but it looks sed counts line numbers from the head & not from tail.. May be I need a wc -l frist then apply if statement & pass on the line count to the sed statement.. :confused:

Can you please suggest me a way to do..

I am using Solaris 10 box...

Maybe some like this works

awk '(s-NR)<500' s=$(cat file | wc -l) file
1 Like

man tail

1 Like

Yes, why make it complex when there are commands to do this.
Maybe I am in love with awk :slight_smile:
tail -n 500

tail is not deleting.. various use of tail is just displaying..

awk '(s-NR)<500' s=$(cat file | wc -l) file

is returning

"awk: can't open 664"

664 is actually the line number..

cat t

one
two
three
four
five
six
seven
eight
nine
ten
tail -n 3 t
eight
nine
ten
awk '(s-NR)<3' s=$(cat t | wc -l) t
eight
nine
ten
1 Like

My purpose is to delete the lines except the last few..

For viewing the certain last few I would have used smaller one:

tail -3 t

This is how to delete

tail -n 500 oldfile > newfile

newfile then contains what you want.

1 Like

Most efficient is to read the file once and store the lines in a circular buffer.
An attempt with awk

awk '
{s[i++]=$0}
{i=i%500}
(i in s){print s}
' file

Perl with its compact arrays might save some bytes of memory.
But there is certainly an option in head or tail ...

1 Like

This removes the 500-1 last line. He like to save the last 500 lines, not delete.

1 Like

Oh sorry, then it's simply

tail -500 file

For academic interest, the circular buffer is still to be used but must be printed at the very end

awk '
{s[NR%n]=$0}
END {
for (i=NR+1;i<=NR+n;i++) {if (i%n in s) print s[i%n]}
}
' n=500 file
1 Like

Not just academic interest. With some implementations of tail the internal buffer is so small that it may not be able to handle 500 lines...

--
Alternative circular buffer (on Solaris use /usr/xpg4/bin/awk , rather than awk ):

awk 'NR>n{sub("[^" RS "]*" RS,x,buf)} {buf=buf $0 RS} END{printf "%s",buf}' n=500 file

--
Without a buffer:

awk 'NR==FNR{next} FNR==1{m=NR-1} FNR>m-n' n=500 file file

or

awk 'NR>m-n' m="$(wc -l<file)" n=500 file
1 Like

Another approach:

[ $( wc -l < file ) -gt 500 ] && tac file | head -500 | tac
1 Like

For academic interest, I have bug-fixed my 2nd sample.
It also makes use of NR instead of an extra variable.

Scrutinizer presented a fix for Jotne's 1st sample

awk '(s-NR)<500' s="$(wc -l <file)" file

The bug occured with certain versions of wc that print a leading space.

1 Like
$ tac file | awk 'NR<=500' | tac
1 Like
printf '%s\n' 1,-500d w q | ed -s file 2>/dev/null

Regards,
alister

1 Like

Thanks all..

I see many responses!! .. :slight_smile:

I will try them one by one will respond by the end of the day..

Thank you all again...

tac not found in my box

man tac
No manual entry for tac.

Below one worked perfectly & really smart way indeed :slight_smile: :

printf '%s\n' 1,-500d w q | ed -s file 2>/dev/null