Linux连接多个具有多列的文件

时间:2014-08-29 16:41:19

标签: linux join awk sed merge

我有一个问题需要合并几个不同的文件。

仅举两个文件*但是Column只是不同的值。 每个文件都有侧面的制表符分隔符。 按ID信息加入所有列。

第一个文件Test.txt

    ID     ID2     ID3    Name  Telephone       
    1       A       +     John    011
    1       B       -     Mike    012
    2       C       +     Sam    013
    3       A       -     Jena    014
    4       B       +     Peter    015

第二个文件Test2.txt

    ID     ID2     ID3    Name  Telephone       
    2       C       +     Henry    013
    3       A       -     Ho    014
    1       A       +     Jamy    011
    1       B       -     Mark    012
    4       B       +     Jung    015

然后是最终结果

    ID     ID2     ID3    Name  Telephone    Name  Telephone
    1       A       +     John    011        Jamy    011
    1       B       -     Mike    012        Mark    012
    2       C       +     Sam     013        Henry   013
    3       A       -     Jena    014        Ho      014
    4       B       +     Peter   015        Jung    015 

因此,组合取决于ID 1 ID2 ID3,

我尝试使用join之类的 join -a1 -a2 -a3 Test1.txt Test2.txt> Test3.txt

这样的东西但是Performance和多文件连接存在问题 而且我不确定这是否正确加入。

有没有人有最好的想法?

3 个答案:

答案 0 :(得分:2)

awk -F"\t" -v OFS="\t" '
    {key = $1 SUBSEP $2 SUBSEP $3}
    FNR==NR {line[key]=$0; next} 
    key in line {print line[$1,$2,$3], $4, $5}
' Test.txt Test2.txt 
ID  ID2 ID3 Name    Telephone   Name    Telephone
2   C   +   Sam 013 Henry   013
3   A   -   Jena    014 Ho  014
1   A   +   John    011 Jamy    011
1   B   -   Mike    012 Mark    012
4   B   +   Peter   015 Jung    015

如果要对输出进行排序,请将输出通过管道传输到| { read header; echo "$header"; sort; }

使用join,您只能加入一个字段。你不得不求助于

join -j1 -t$'\t' <(sed 's/\t/:/;s/\t/:/' Test.txt|sort) \
                 <(sed 's/\t/:/;s/\t/:/' Test2.txt|sort) | 
sed 's/:/\t/;s/:/\t/'

然后,将标题留在底部(您可以使用| tac | { read header; echo "$header"; tac; }修复


对评论的回应:

awk -F"\t" '
    {key = $1 FS $2 FS $3}
    NR == 1 {header = key}
    !(key in result) {result[key] = $0; next}
    { for (i=4; i <= NF; i++) result[key] = result[key] FS $i }
    END {
        print result[header]
        delete result[header]
        PROCINFO["sorted_in"] = "@ind_str_asc"    # if using GNU awk
        for (key in result) print result[key]
    }
' Test.txt Test2.txt  # ... and other files

答案 1 :(得分:1)

使用GNU bash,GNU核心实用程序和GNU awk:

join -j 5 <(sort -n Test.txt) <(sort -n Test2.txt) | awk '{print $2,$3,$4,$5,$1,$9,$1}' | column -t

输出:

ID  ID2  ID3  Name   Telephone  Name   Telephone
1   A    +    John   011        Jamy   011
1   B    -    Mike   012        Mark   012
2   C    +    Sam    013        Henry  013
3   A    -    Jena   014        Ho     014
4   B    +    Peter  015        Jung   015

答案 2 :(得分:1)

使用awk,您只需为文件中显示的唯一键构建字符串即可。然后,您可以将输出传输到column -t以进行漂亮打印。

我已使用第1,2和3列作为键,并将每个文件的剩余列构建到原始行。

awk --re-interval -F"\t" '
{ key = $1 SUBSEP $2 SUBSEP $3 }
{
    if (line[key]) {
        sub (/([^\t]+\t+){3}/,"");
        line[key] = line[key] FS $0
    }
    else {
        line[key] = $0
    }
}
END {
     for (key in line) print line[key]
}' file* | column -t | sort -r
ID  ID2  ID3  Name   Telephone  Name   Telephone
4   B    +    Peter  015        Jung   015
3   A    -    Jena   014        Ho     014
2   C    +    Sam    013        Henry  013
1   B    -    Mike   012        Mark   012
1   A    +    John   011        Jamy   011

注意:如果您使用的是GNU awk v4或更高版本或BSD awk,则无需指定--re-interval


如果您对perl开放,那么您可以一次性完成:

perl -F"\t" -lane '
    $" = "\t";
    $key = "@F[0..2]";
    push @{ $line{$key} }, @F[3..$#F];
}{
    print join "\t", $_, @{ $line{$_} } for grep { $_ =~ /ID/ } sort keys %line;
    print join "\t", $_, @{ $line{$_} } for grep { not $_ =~ /ID/ } sort keys %line
' file*