I want to print all the lines except the last three lines from the input through awk only. Please note that my file contains n number of lines.
For example,
file.txt
contains,
foo
bar
foobar
barfoo
last
line
I want the output to be,
foo
bar
foobar
I know it could be possible through the combination of tac
and sed
or tac
and awk
$ tac file | sed '1,3d' | tac
foo
bar
foobar
$ tac file | awk 'NR==1{next}NR==2{next}NR==3{next}1' | tac
foo
bar
foobar
But i want the output through awk only.
Best Answer
It's ever-so clunky but you can add every line to an array and at the end —when you know the length— output everything but the last 3 lines.
Another (more efficient here) approach is manually stacking in three variables:
a
only prints after a line has moved fromc
tob
and then intoa
so this limits it to three lines. The immediate upsides are it doesn't store all the content in memory and it shouldn't cause buffering issues (fflush()
after printing if it does) but the downside here is it's not simple to scale this up. If you want to skip the last 100 lines, you need 100 variables and 100 variable juggles.If awk had
push
andpop
operators for arrays, it would be easier.Or we could pre-calculate the number of lines and how far we actually want to go with
$(($(wc -l < file) - 3))
. This is relatively useless for streamed content but on a file, works pretty well:Typically speaking you'd just use
head
though:Using terdon's benchmark we can actually see how these compare. I thought I'd offer a full comparison though:
head
: 0.018s (me)awk
+wc
: 0.169s (me)awk
3 variables: 0.178s (me)awk
double-file: 0.322s (terdon)awk
circular buffer: 0.355s (Scrutinizer)awk
for-loop: 0.693s (me)The fastest solution is using a C-optimised utility like
head
orwc
handle the heavy lifting things but in pureawk
, the manually rotating stack is king for now.