2

H, I want to replace specific character matches (.) in all rows, using the first line as a reference

My attempt at re-hashing an answer I got to a different question:

awk -F'|' 'BEGIN{OFS=FS} NR==1 {for(i=1;i<=NF;i++) a[$i] } NR>1 {for(i in a) if( $i == "\." ) $i="a"}1'

...the idea of the re-work I tried in the above code was to store the first row characters in 'a', and then when seeing a '.' in rows>1 changing the '.' to the corresponding column character stored in 'a'. But its not worked.

Input:

A|N|G|O|T|T|T|P|G|C|Q|A|R|A|S|G|U|V|T|T
.|C|G|A|T|T|.|.|G|C|.|.|.|A|C|R|C|.|T|T
A|.|.|.|N|.|T|T|N|.|.|A|C|.|.|R|.|.|.|.

Desired Output:

A|N|G|O|T|T|T|P|G|C|Q|A|R|A|S|G|U|V|T|T
A|C|G|A|T|T|T|P|G|C|Q|A|R|A|C|R|C|V|T|T
A|N|G|O|N|T|T|T|N|C|Q|A|C|A|S|R|U|V|T|T
6

Right idea - wrong implementation

  1. you need to store the field values in an array indexed by the field position. So instead of a[$i], make that a[i]=$i

  2. then you need to look up the values by index in the array. So not $i="a" but $i=a[i]

As an aside, $i == "\." isn't a regular expression test, so you don't need to escape .

$ awk -F'|' 'BEGIN{OFS=FS} NR==1 {for(i=1;i<=NF;i++) a[i]=$i } NR>1 {for(i in a) if( $i == "." ) $i=a[i]}1' file
A|N|G|O|T|T|T|P|G|C|Q|A|R|A|S|G|U|V|T|T
A|C|G|A|T|T|T|P|G|C|Q|A|R|A|C|R|C|V|T|T
A|N|G|O|N|T|T|T|N|C|Q|A|C|A|S|R|U|V|T|T

As Ed Morton pointed out, you can improve the solution by replacing the explicit loop using the awk built-in split function:

awk -F'|' 'BEGIN{OFS=FS} NR==1 {split($0,a)} NR>1 {for(i in a) if( $i == "." ) $i=a[i]}1'
| improve this answer | |
  • Thanks steeldriver, it worked great and I really appreciate the explanation, as most importantly your answer has improved my understanding – Giles Mar 30 at 18:03
  • 1
    @EdMorton good point - added, thanks – steeldriver Mar 30 at 18:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.