Объединение двух файлов с помощью awk

у меня есть два файла, как показано ниже, которые являются разделителями табуляции:

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

файл B

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

Я хочу присоединиться к двум файлам на основе 3 столбцов с "chr1", " 123 " и "aa" и добавить первые два столбца из файла B в файл A, чтобы вывод выглядел так, как показано ниже: вывод:

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

может кто-нибудь помочь сделать это в awk. Если возможно, используя awk oneliners?

2 ответов


вот один подход с использованием awk:

$ awk 'NR==FNR{a[,]=OFS;next}{=a[,];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 

объяснение:

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

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

Edit:

для задачи 3 поля вы просто добавляете дополнительное поле:

$ awk 'NR==FNR{a[,,]=OFS;next}{=a[,,];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 

можно использовать join, но конвейер становится настолько сложным, что может быть проще переключиться на более мощный язык, такой как 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/ \+\([^ ]\+\) \+\([^ ]\+\)/ :/' fileC | sort -k2) \
 | sed 's/:/ /'

Perl решение, используя хэш, чтобы запомнить всю информацию:

#!/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";
}