Search code examples
bashunixawkkshnawk

How to delete last two lines of an output using AWK?


i am trying to make a graphic representation of an output that shows the free memory of the server of the current day (which is shown approximetaly by hour), from that output i dont want the first two lines and last two, the first two i can easy delete with the NR, though i have had trouble with the last two, the only way i found was to reverse the whole output delete the first two lines and then reverse it again to its normal state.

Any better/correct way to delete the last two lines but do it inside the AWK?

this is what i have so far and works fine.

echo 'graphics (freemem)'
echo 'GiB      0      500k      1M      1,5M'
sar -r | tail -r | awk 'NR > 2{ print }' | tail -r | nawk '
    NR > 4{
    LINE = sprintf("%" int(($2 / 100000)*2 )"s", " "); 
    gsub(/ /, "#", LINE);
    print $1, LINE; } 
'

UPDATE

So i did it in another way because it would easier the script to read and it works for me, but for any other guy that needs to do that use either the previous way i did it, or the ways that the guys below have answered.

echo 'graphics (freemem)'
echo 'GiB      0      500k      1M      1,5M'
sar -r | nawk '
    NR > 4 && $1 !~ /Average/ && $1 !~ /^$/ {
    LINE = sprintf("%" int(($2 * 2) / 100000 )"s", " "); 
    gsub(/ /, "#", LINE);
    print $1, LINE; } 
'

UPDATE 2

So what i want to make is a graphic representation of data monitored, this output updates about each hour, keep in mind that in different hours of the day the length of the rows vary. So this the actual ouput of sar -r that i get.

.
SunOS doc 5.10

00:00:00 freemem freeswap
01:00:01 1624430 19474799
02:00:00 1624421 19474738
03:00:00 1624413 19474683
04:00:00 1624527 19475309
05:00:00 1624525 19475290
06:00:00 1624517 19475232
07:00:01 1624510 19475176
08:00:00 1624503 19475120
08:20:00 1539793 18771472
08:40:00 1509924 18535201
09:00:00 1500681 18492050
09:20:00 1494254 18435535
09:40:00 1479623 18266664
10:00:00 1486372 18317241
10:20:00 1480565 18269032
10:40:00 1479030 18260247
11:00:01 1476462 18237731
11:20:00 1475999 18234221
11:40:00 1478854 18259827
12:00:00 1478223 18254861
12:20:00 1482956 18294572
12:40:00 1479061 18260565
13:00:00 1470463 18185441
13:20:00 1474090 18217961
13:40:00 1478585 18257393
14:00:00 1478219 18255445
14:20:00 1474327 18223749
14:40:00 1470468 18190676
15:00:00 1471941 18204282
15:20:00 1474138 18223380
15:40:00 1471698 18202561
16:00:00 1470316 18191099
16:20:00 1467203 18163653
16:40:00 1468943 18179148

Average  1549994 18855701

So we dont care about freeswap and we dont use that. We care only for the results of freemem. So what i am trying to do is take as entry that column and "translate" the number into " # " through a calculation so i can later specify the amount of data in the graphic representation. Though, there are rows in the start and in the end of the output that i dont need so i need them deleted.

This is the working code i have now

echo '\ngraphics (freemem)'
echo 'GiB      0      500k      1M      1,5M'
sar -r | awk 'NR > 2 {print P2} {P2=P; P=$0}' |
nawk '
    NR > 4 {
    LINE = sprintf("%" int(($2 * 2) / 100000 )"s", " "); 
    gsub(/ /, "#", LINE);
    print $1, LINE; } 
'

so the correct output is this one:

graphics (freemem)
GiB      0      500k      1M      1,5M
01:00:01 ################################
02:00:00 ################################
03:00:00 ################################
04:00:00 ################################
05:00:00 ################################
06:00:00 ################################
07:00:01 ################################
08:00:00 ################################
08:20:00 ##############################
08:40:00 ##############################
09:00:00 ##############################
09:20:00 #############################
09:40:00 #############################
10:00:00 #############################
10:20:00 #############################
10:40:00 #############################
11:00:01 #############################
11:20:00 #############################
11:40:00 #############################
12:00:00 #############################
12:20:00 #############################
12:40:00 #############################
13:00:00 #############################
13:20:00 #############################
13:40:00 #############################
14:00:00 #############################
14:20:00 #############################
14:40:00 #############################
15:00:00 #############################
15:20:00 #############################
15:40:00 #############################
16:00:00 #############################
16:20:00 #############################

Solution

  • You can store the line, do things and print after a while so that you can know if you are in the last line or not. But that is too much golfing when you have tools like head and tail.

    Just pipe the output to head before passing it to awk:

    head -n -2
    

    Test

    $ seq 10
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $ seq 10 | head -n -2
    1
    2
    3
    4
    5
    6
    7
    8
    

    Since you don't have a GNU head allowing you to do this, you can hold the variables with awk:

    awk '{if (prev_prev_line) print prev_prev_line; prev_prev_line=prev_line}
         {prev_line=$0}'
    

    This shifts lines, so that you have in prev_prev_line the content of the line you had two lines ago:

    NR      $0    prev_line   prev_prev_line
     1      1      -             -
     2      2      1             -
     3      3      2             1
     4      4      3             2
     ....
     NR-1   NR-1   NR-2          NR-3
     NR     NR     NR-1          NR-2
    

    So since you are working with prev_prev_line, you will end up handling from the 1st to the (NR-2)th line.

    So in prev_prev_line you have the line that occurred two lines ago. This way, you do not use the last two lines in the output.

    See how it works with an example:

    $ awk '{if (prev_prev_line) print NR,prev_prev_line; prev_prev_line=prev_line}
             {prev_line=$0}' < <(seq 10)
    3 1
    4 2
    5 3
    6 4
    7 5
    8 6
    9 7
    10 8