2014-08-29 111 views
1

我有一個問題需要合併多個不同的文件。Linux加入多列多文件

只是例如兩個文件*但列是相同的只是不同的值。 每個文件都有側邊的製表符分隔符。 按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,

我試着用j OIN像 加入-A1-A2-A3個Test1.txt的test2.txt> Test3.txt

像這樣的事情,但有一個與性能和多個文件的問題加入 而且我不知道這是否正確連接。

有沒有人有最好的想法?

回答

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

Awsome幾乎接近,但正如我提到它可能是多個文件,是否有可能打印所有列一些靈活的命令,而不是1美元,2美元,3美元,4美元,5美元?謝謝!! – 2014-08-29 18:46:22

+1

鍵同樣是$ 1,$ 2,$ 3,但其他列可以是多個 – 2014-08-29 18:47:41

+2

答案更新。你應該更新你的問題,這樣需求就更加明顯了(例如,向Test2添加一列)。 – 2014-08-29 19:26:14

1

隨着GNU的bash,GNU core工具和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 
+0

'join'詞彙要排序的文件。 – 2014-08-29 17:44:39

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* 
+0

當我使用三個輸入文件測試此解決方案時,我看到的輸出與您所顯示的不同。在我的輸出中,我看到每個輸入文件包含一次ID列。 – 2014-08-29 19:34:00

+0

@ carl.anderson你在做什麼操作系統?我測試了3個文件,基本上一遍又一遍地提供相同的文件,它工作正常。我認爲這個問題可能與打印出來的標題行不一致。 – 2014-08-29 19:49:48

+0

OSX。在Ubuntu(精確)我的awk甚至沒有--re-interval標誌。無論如何,這應該沒有關係。您是否使用_tab_分隔的文件,如OP指定的那樣?如果沒有,那可能會導致'sub'命令不做任何事情,包括超過你的意圖。 – 2014-08-29 19:58:04