0

I would like to replace one line in a text file with the corresponding line in another text file.

Contents of File1:

>line1  
GGG  
>line2  
AAA  
>line3  
TTT  
>line4  
CCC  
>line5  
CAT 

Contents of File2:

>name.A
CTGG  
>name.B
GACC  
>name.C
CTGG  
>name.D
GGAA  
>name.E
GCTA

There are the same number of lines in both files. The final file should be as follows:

>name.A  
GGG   
>name.B  
AAA  
>name.C  
TTT  
>name.D  
CCC  
>name.E  
CAT 

I've searched for a solution but can't seem to find one. I did try the following:

awk 'FNR==NR {data[FNR]=$0; next} (FNR%2==1){$0=data[FNR]}1' < File1 < File2

but nothing happens.

Any help would be much appreciated!

  • This looks like fasta sequence files. Are your sequences always on a single line? – Kusalananda Jul 20 at 16:01
  • i format them so that they are when i'm messing with them in sed/awk. – Emily Rowlands Jul 20 at 16:19
  • 1
    @EmilyRowlands don't lump awk in with sed, they are completely different tools and among many differences one is line-based (sed) while the other is record-based (awk). You do not need to pre-process your files to change the format if you're using awk. – Ed Morton Jul 20 at 16:29
1
$ awk 'NR==FNR{a[NR]=$0; next} {print (FNR%2 ? $0 : a[FNR])}' file1 file2
>name.A
GGG
>name.B
AAA
>name.C
TTT
>name.D
CCC
>name.E
CAT

As well as being more concise (and working :-) ), the above is more efficient than the approach you had because:

  1. Its not assigning to $0 and so isn't forcing awk to resplit the record into fields
  2. It's not testing a second condition (1) for every line before deciding to print or not.

Also - don't use input redirection to open files for awk as it doesn't work for multiple files (as you found out) and robs you of the ability to check the FILENAME.

0

Your awk command is basically correct - it will work if you remove the extraneous redirections and swap the file order:

$ awk 'FNR==NR {data[FNR]=$0; next} (FNR%2==1){$0=data[FNR]}1' File2 File1
>name.A
GGG  
>name.B
AAA  
>name.C
TTT  
>name.D
CCC  
>name.E
CAT 

Alternatively, with GNU sed and a shell that supports process substitution:

$ sed -e '2~2{R /dev/stdin' -e 'd}' File2 < <(sed '1~2d' File1)
>name.A
GGG  
>name.B
AAA  
>name.C
TTT  
>name.D
CCC  
>name.E
CAT 

Explanation:

  • sed '1~2d' File1 delete odd lines from File1

  • < <(...) feed the results via stdin

  • sed -e '2~2{R /dev/stdin' -e 'd}' File2 read one line at a time from stdin and queue it to be inserted after the next even line of File2; then delete the even line

  • i'm using terminal on macOS and i get an error when i use that command: sed: 1: "2~2{R /dev/stdin ": invalid command code ~ sed: 1: "1~2d": invalid command code ~ – Emily Rowlands Jul 20 at 15:51
  • @EmilyRowlands in that case you presumably don't have GNU sed – steeldriver Jul 20 at 15:52
  • the updated awk command works like a charm. thank you SO much!! fwiw i did go ahead and try your initial command with gsed and got the following error: sed: 1: "1~2d": invalid command code ~ >name.A >name.B >name.C >name.D >name.E i suppose gsed doesn't completely mirror the functionality of GNU sed? – Emily Rowlands Jul 20 at 15:57
0

Try it like this, it's complicated but works. Maybe there is an easier solution.

  • First separate odds and even lines:

    awk '{print>"file1-"NR%2}' file1 awk '{print>"file2-"NR%2}' file2

This will create file1-0, file1-1, file2-0, file2-1 In my case and from your example, file1-0 had data you needed from file1 and file2-1 had data you needed from file2, check output of those files bellow

[mtodoric@bulletproof test]$ cat file1-0
GCCAAACAGCTAGCTTGA
GCCAAACAGCTAGCTTGATTAATAA
GCCAAACAGCTAGCTTGATTAATAATATAA
GCCAACCAGCTAGCTTAA
GCTAACCAGCTAGCTTAA
GCTGACTAGCTAGCCAACATATTT
GCCCAACAGCTAGCCCACTCA
GCCAACCAGCTAGCTTAAT
GCCAACCAGCTAGCTTAATT

[mtodoric@bulletproof test]$ cat file2-1
Siphateles.boraxobius_complete-mitochondrion_harney-county-or.fasta
Siphateles.boraxobius_complete-mitochondrion_harney-county-or.fasta
Siphateles.alvordensis_complete-mitochondrion_harney-county-or.fasta
Rhinichthys.evermanni_complete-mitochondrion_douglas-county-or.fasta
Oregonichthys.kalawatseti_complete-mitochondrion_linn-county-or.fasta
Oncorhynchus.mykiss.irideus_complete-mitochondrion_douglas-county-or.fasta
Cottus.perplexus_complete-mitochondrion_douglas-county-or.fasta
Oncorhynchus.mykiss.irideus_complete-mitochondrion_douglas-county-or.fasta
Cottus.perplexus_complete-mitochondrion_douglas-county-or.fasta
  • Now do a "oneliner" that will combine the data.

    RB=1; while read line; do echo $line >> final.txt; sed -n ${RB}p file1-0 >> final.txt; RB=$((${RB}+1)); done < file2-1

This will basically read from file2-1 (since that file had the first line from your example output), echo each line into another file (final.txt), but also, thanks to variable RB will get that line number from another file and print stdout to final.txt, and will increase the value of RB so it can increment the line on next loop.

Replace names of file2-1 and file1-0 in "one liner" to correspond your data.

Hope this helps it.

0

Tried with sed command and it worked fine

for ((i=2;i<=10;i++));do j=`sed -n ''$i'p' f1`; k=`echo $j|sed -r "s/\s+//g"`;sed -i ""$i"s/.*/"$k"/g" f2;i=$(($i+1)); done

output

>name.A
GGG
>name.B
AAA
>name.C
TTT
>name.D
CCC
>name.E
CAT
0

You could do your pbm the following way by first invoking the paste utility to arrange the lines in a TAB separated manner and then call upon the sed editor to perform surgery to obtain the desired data output:

$ paste File1 File2  | sed -ne '
    s/>.*\t//p
    s/\t.*//p
 '

Output

>name.A
GGG  
>name.B
AAA  
>name.C
TTT  
>name.D
CCC  
>name.E
CAT 

Note: Posix sed does not recognize \t escape sequence to stand in for the tab char. I just used it to highlight an otw invisible char. So you place a literal tab in place of the \t.

With Perl utility, it is a one-liner:

$ perl -pe '$_=($_,$,.<STDIN>)[$.%2]' File1 < File2

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.