2012-11-06 25 views
6

Mam dwa pliki, jak pokazano poniżej, które są tabulatorami:połączyć dwa pliki za pomocą awk

file

chr1 123 aa b c d 
chr1 234 a b c d 
chr1 345 aa b c d 
chr1 456 a b c d 
.... 

plik B

xxxx abcd chr1 123 aa c d e 
yyyy defg chr1 345 aa e f g 
... 

Chcę połączyć dwa pliki na podstawie 3 kolumn z "chr1", "123" i "aa" i dodaj pierwsze dwie kolumny z pliku B do pliku A, tak, aby wynik wyglądał jak pokazano poniżej: output:

chr1 123 aa b c d xxxx abcd 
chr1 234 a  b c d 
chr1 345 aa b c d yyyy defg 
chr1 456 a b c d 

Czy ktoś może pomóc w tym w awk. Jeśli to możliwe, używając awk oneliners?

+5

[Co próbowałeś do tej pory] (http://whathaveyoutried.com/)? – doublesharp

Odpowiedz

11

Oto jeden podejście korzystając awk:

$ awk 'NR==FNR{a[$3,$4]=$1OFS$2;next}{$6=a[$1,$2];print}' OFS='\t' fileb filea 
chr1 123  a b c  xxxx abcd 
chr1 234  a b c 
chr1 345  a b c  yyyy defg 
chr1 456  a b c 

Objaśnienie:

NR==FNR    # current recond num match the file record num i.e in filea 
a[$3,$4]=$1OFS$2 # Create entry in array with fields 3 and 4 as the key 
next    # Grab the next line (don't process the next block) 
$6=a[$1,$2]   # Assign the looked up value to field 6 (+rebuild records) 
print    # Print the current line & the matching entry from fileb ($6) 

OFS='\t'   # Seperate each field with a single TAB on output 

Edit:

Dla problemu 3 pola ty prosty dodać dodatkowe pole:

$ awk 'NR==FNR{a[$3,$4,$5]=$1OFS$2;next}{$6=a[$1,$2,$3];print}' OFS='\t' fileb filea 
chr1 123 aa  b  c  xxxx  abcd 
chr1 234 a  b  c 
chr1 345 aa  b  c  yyyy  defg 
chr1 456 a  b  c 
+0

Mam zmodyfikowany oryginalny problem. Coudl zapewniasz rozwiązanie tego. – chas

+0

Po prostu dodajesz dodatkowe pole, patrz edycja. –

+0

Dzięki. Dodałem dodatkowe pole w następujący sposób, które poszło nie tak: awk 'NR == FNR {a [3 $, 5 $, 5 $] = 1 $ FS $ 2OFS $ 3, następnie} {6 $ = a [1, 2 $]; 'OFS =' \ t 'plik filea. – chas

2

Można użyć join, ale potok staje się tak skomplikowany, że łatwiej będzie przełączyć się na bardziej wydajny język, taki jak Perl.

join -11 -21 -o1.1,1.2,1.3,1.4,1.5,2.4,2.5 \ 
    <(sed 's/ \+/:/' fileA | sort) \ 
    <(sed 's/ \+/:/' fileB | sort) \ 
| join -11 -22 -a1 -o1.1,1.2,1.3,1.4,1.5,1.6,1.7,2.5,2.6 \ 
    - <(sed 's/ \+\([^ ]\+\) \+\([^ ]\+\)/ \1:\2/' fileC | sort -k2) \ 
| sed 's/:/ /' 

rozwiązanie Perl, używając skrótu zapamiętać wszystkie informacje:

#!/usr/bin/perl 
use warnings; 
use strict; 

#    key_start key_end keep_from output 
my %files = (A => [0,  1,  2,  [0 .. 3]], 
      B => [0,  1,  2,  [-2, -1]], 
      C => [1,  2,  3,  [-2, -1]], 
      ); 

my %hash; 

for my $file (keys %files) { 
    open my $FH, '<', "file$file" or die "file$file: $!"; 
    while (<$FH>) { 
     my @fields = split; 
     $hash{"@fields[$files{$file}[0], $files{$file}[1]]"}{$file} 
      = [ @fields[$files{$file}[2] .. $#fields] ]; 
    } 
} 

for my $key (sort keys %hash) { 
    print $key, join(' ', q(), 
        grep defined, map { 
         @{ $hash{$key}{$_} }[@{ $files{$_}[-1] }] 
        } sort keys %files), "\n"; 
} 
+0

@ user1779730: Dodano rozwiązanie Perla. – choroba