3

I need a script to print last n rows of a text file. the text file names and number of rows can be varied and I want to call only script to print last n rows of any text files. I know for first n row I can use NR < n; print but how can I do it for last n row as number of rows can be varied.- thanks

Sajjad
  • 67
  • 2
  • 8
  • Possible duplicate of [Implement tail with awk](http://stackoverflow.com/questions/9101296/implement-tail-with-awk) – tripleee Apr 04 '16 at 07:13

5 Answers5

2

There is a unix tool for exactly that purpose, called tail. To get the last 100 rows of file, you would use tail -n 100 file, then use the output directly or pipe it to other programs like awk.

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
Lars Fischer
  • 9,135
  • 3
  • 26
  • 35
  • Yes, I know about tail but I do not know how to write in in a script! Sorry I am so new in awk! – Sajjad Apr 03 '16 at 19:56
  • @Sajjad If you really need tails functionality inside awk, see for example http://stackoverflow.com/questions/9101296/implement-tail-with-awk – Lars Fischer Apr 03 '16 at 20:02
2

To do this natively in awk, you have to remember the lines as you see them:

awk -v n=10 '
    {line[NR]=$0}
    END {for (i=NR-(n-1); i<=NR; i++) print line[i]}
' file

To save memory, we don't need to remember the whole file; use

    {line[NR]=$0; if (NR>n) delete line[NR-n]}

However it is simpler to reverse the file, print the first n lines, and re-reverse the output

tac file | awk -v n=10 'NR <= n' | tac

But using tail is much simpler that all that

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
1

as an exercise, there is another version trading space vs time to achieve the same

$ awk -v n=10 'NR==FNR{a=NR;next} FNR>(a-n)' file{,}

scan the files first to get the number of lines and use to filter last n rows second time.

karakfa
  • 66,216
  • 7
  • 41
  • 56
1
$ cat file
1
2
3
4

$ cat tst.awk
{ rec[NR % n] = $0 }
END {
    for (i=NR+1+(n<NR?0:n-NR); i<=(NR+n); i++) {
        print rec[i % n]
    }
}

$ awk -v n=2 -f tst.awk file
3
4

The complexity when setting the starting value of i is to accommodate cases where you ask to print more records than exist in the file, e.g.:

$ awk -v n=6 -f tst.awk file
1
2
3
4
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
0

Give this script a try:

{
  lines[(i=(++i%n))]=$0;
}
END {
  if (NR>=n) {
    linessize=n;
  } else {
    linessize=NR;
    i=0;
  }
  for(j=1;j<=linessize;j++) {
    print lines[(i+j)%n];
  }
}

File is parsed only once.

An array with n elements only is used to buffer the lines read.

The tests:

$ printf "one line\n2nd line\n" | ./tail-awk.awk -f script.awk -v n=10
one line
2nd line
$ ./tail-awk.awk  -f script.awk -v n=10 <(man bash)
       attempted.  When a process is stopped, the shell immediately executes the next command in the sequence.  It  suffices  to
       place the sequence of commands between parentheses to force it into a subshell, which may be stopped as a unit.

       Array variables may not (yet) be exported.

       There may be only one active coprocess at a time.



GNU Bash-4.1                                            2009 December 29                                                 BASH(1)
$ ./tail-awk.awk -f script.awk -v n=5 /etc/apt/sources.list

deb http://archive.debian.org/debian-archive hamm main

deb ftp://ftp.debian.org/debian squeeze contrib

$
Jay jargot
  • 2,745
  • 1
  • 11
  • 14
  • Thanks. It worked. How can I edited to call it as a script by using this 'awk -f script.awk file.txt' – Sajjad Apr 03 '16 at 22:48