2

I am new to linux and awk scripting. I have tab delim txt file like follows:

AAA   134  145  Sat    150   167
AAA   156  167  Sat    150   167
AAA   175  187  Sat    150   167 

I would like replace only the value in last row, second column(175) with the value in the last row,5th column(150+1) so that my final output should look like

AAA   134  145  Sat    150   167
AAA   156  167  Sat    150   167
AAA   151  187  Sat    150   167

I tried awk '$2=$5+1' file.txt but it changes all the values in second column which I don't want. I want replace only 175 with 150(+1). Kindly guide me

melpomene
  • 84,125
  • 8
  • 85
  • 148
Carol
  • 367
  • 2
  • 3
  • 18
  • Please read the book Effective Awk Programming, 4th Edition, by Arnold Robbins to get a basic understanding of awk. Your past few questions suggest you are trying to learn awk by trial and error with no foundation which is a terrible idea. – Ed Morton Jul 31 '16 at 01:05

2 Answers2

3

The difficulty is that, unlike sed, awk does not tell us when we are working on the last row. Here is one work-around:

$ awk 'NR>1{print last} {last=$0} END{$0=last;$2=$5+1;print}' OFS='\t' file.txt
AAA     134     145     Sat     150     167
AAA     156     167     Sat     150     167
AAA     151     187     Sat     150     167

This works by keeping the previous line in the variable last. In more detail:

  • NR>1{print last}

    For every row, except the first, print last.

  • last=$0

    Update the value of last.

  • END{$0=last; $2=$5+1; print}

    When we have reached the end of the file, update field 2 and print.

  • OFS='\t'

    Set the field separator on output to a tab.

Alternate method

This approach reads the file twice, first to count the number of lines and the second time to change the last row. Consequently, this is less efficient but it might be easier to understand:

$ awk -v n="$(wc -l <file.txt)" 'NR==n{$2=$5+1} 1' OFS='\t' file.txt
AAA     134     145     Sat     150     167
AAA     156     167     Sat     150     167
AAA     151     187     Sat     150     167

Changing the first row instead

$ awk 'NR==1{$2=$5+1} 1' OFS='\t' file.txt
AAA     151     145     Sat     150     167
AAA     156     167     Sat     150     167
AAA     175     187     Sat     150     167

Changing the first row and the last row

$ awk 'NR==1{$2=$5+1} NR>1{print last} {last=$0} END{$0=last;if(NR>1)$2=$5+1;print}' OFS='\t' file.txt
AAA     151     145     Sat     150     167
AAA     156     167     Sat     150     167
AAA     151     187     Sat     150     167
John1024
  • 109,961
  • 14
  • 137
  • 171
  • Thanks. It works fine. Similarly, if I want to change the first row second column value, how can I do that – Carol Jul 30 '16 at 20:44
  • 1
    @Carol I just added code for the first row to the answer. – John1024 Jul 30 '16 at 20:51
  • 1
    FYI `END{$2=$5+1;print}` relies on undefined behavior per POSIX so it'll only work in some awks. POSIX only guarantees that NF will still be populated in the END section with the value from the last-read record, not $0, $1, etc. If you care, for portability start with `END{$0=last;...}` – Ed Morton Jul 31 '16 at 04:06
  • @EdMorton Interesting. Answer updated with `$0=last`. I tried gawk, mawk, and original-awk without apparent issue but being POSIX compliant is better. Thanks! – John1024 Jul 31 '16 at 06:25
  • @EdMorton@John1024 COuld you suggest me a god tutorial to master awk – Carol Jul 31 '16 at 06:47
  • @Carol Because it is clear and concise, I like the [Grymoire tutorial](http://www.grymoire.com/Unix/Awk.html). For a much more extensive and up-to-date tutorial on GNU awk, see [Effective Awk Programming (PDF)](https://www.gnu.org/software/gawk/manual/gawk.pdf) – John1024 Jul 31 '16 at 07:11
  • 1
    Buy the book Effective Awk programming, 4th Edition, by Arnold Robbins. @John1024 - FWIW I always recommend people buy the book as IMHO the guy who wrote it and provides GNU awk deserves to make a couple of bucks for his efforts and not be punished for very kindly also making the book available as PDF for reference. – Ed Morton Jul 31 '16 at 17:01
  • @John1024 - FYI the subject of retaining `$0`, etc. in the `END` section just came up in another thread and a couple of folks bothered to investigate which awks have what behavior, see the comments and answer at http://stackoverflow.com/q/38698645/1745001 if you care. – Ed Morton Aug 01 '16 at 13:29
2

@John1024 's answer is very informative.

awk have builtin getline function to process file.

It returns 1 on sucess, 0 on end of file and -1 on an error.

awk '{ 
        line=$0; 
        if (getline == 0 ) {
           $2=$5+1; 
           print $0; 
        } else { 
           print  line RS $0;
        }
     }' OFS='\t' file.txt
sozkul
  • 665
  • 4
  • 10