使用键/联接列中的重复条目执行完全外部联接的命令

时间:2018-11-15 16:10:03

标签: unix awk scripting

我有三个文件。我需要根据一列将它们加入并进行一些转换。

file1.dat (第1列用于连接)

123,is1,ric1,col1,smbc1  
123,is2,ric1,col1,smbc1  
234,is3,ric3,col3,smbc2  
345,is4,ric4,,smbc2  
345,is4,,col5,smbc2 

file2.dat (第1列用于连接)

123,abc  
234,bcd  

file3.dat (第4列用于连接)

r0c1,r0c2,r0c3,123,r0c5,r0c6,r0c7,r0c8  
r2c1,r2c2,r2c3,123,r2c5,r2c6,r2c7,r2c8  
r3c1,r3c2,r3c3,234,r3c5,r3c6,r3c7,r3c8  
r4c1,r4c2,r4c3,345,r4c5,r4c6,r4c7,r4c8   

预期输出(output.dat)

123,r0c5,is1,ric1,smbc1,abc,r0c8,r0c6,col1,r0c7,r0c1,r0c2,r0c3  
123,r0c5,is2,ric1,smbc1,abc,r0c8,r0c6,col1,r0c7,r0c1,r0c2,r0c3  
123,r2c5,is1,ric1,smbc1,abc,r2c8,r2c6,col1,r2c7,r2c1,r2c2,r2c3  
123,r2c5,is2,ric1,smbc1,abc,r2c8,r2c6,col1,r2c7,r2c1,r2c2,r2c3  
234,r3c5,is3,ric3,smbc2,bcd,r3c8,r3c6,col3,r3c7,r3c1,r3c2,r3c3  
345,r4c5,is4,ric4,smbc2,N/A,r4c8,r4c6,N/A,r4c7,r4c1,r4c2,r4c3  
345,r4c5,is4,N/A,smbc2,N/A,r4c8,r4c6,col5,r4c7,r4c1,r4c2,r4c3 

我写了以下awk命令。

awk '
BEGIN {FS=OFS=","}
FILENAME == ARGV[1] { temp_join_one[$1] = $2"|"$3"|"$4"|"$5; next}
FILENAME == ARGV[2] { exchtbunload[$1] = $2; next}
FILENAME == ARGV[3] { s_temp_join_one = temp_join_one[$4];
split(s_temp_join_one, array_temp_join_one,"|");
v3=(array_temp_join_one[1]==""?"N/A":array_temp_join_one[1]);
v4=(array_temp_join_one[2]==""?"N/A":array_temp_join_one[2]);
v5=(array_temp_join_one[4]==""?"N/A":array_temp_join_one[4]);
v6=(exchtbunload[$4]==""?"N/A":exchtbunload[$4]);
v9=(array_temp_join_one[3]==""?"N/A":array_temp_join_one[3]);
v11=($2=""?"N/A":$2);
print $4, $5, v3, v4, v5, v6, $8, $6, v9, $7, $1, v11, $3 >
"output.dat" }
' file1.dat file2.dat file3.dat

我需要加入所有三个文件。

最终输出文件应具有file3中的所有值,而不管它们是否在其他两个文件中;如果其他两个文件中不存在相应的列,则相应的列应为空(或N / A)。 (列的顺序不是很大的问题。我可以使用awk重新排列它们。)

但是我的问题是,由于键不是唯一的,所以无法获得预期的输出。我的输出只有三行。

我尝试应用使用连接条件建议的解决方案。它适用于较小的文件。但是我的文件大小接近3-5 GB。它们是按数字顺序而不是按字典顺序。对它们进行排序看起来会花费很多时间。

任何建议都会有所帮助。

谢谢。

2 个答案:

答案 0 :(得分:4)

使用join,假设文件按键排序。

$ join -t, -1 1 -2 4 <(join -t, -a1 -a2 -e "N/A" -o1.1,1.2,1.3,1.4,1.5,2.1 file1 file2) \
  file3 -o1.1,2.5,1.2,1.3,1.5,1.6,2.8,2.6,1.4,2.7,2.2,2.3

123,r0c5,is1,ric1,smbc1,123,r0c8,r0c6,col1,r0c7,r0c2,r0c3
123,r2c5,is1,ric1,smbc1,123,r2c8,r2c6,col1,r2c7,r2c2,r2c3
123,r0c5,is2,ric1,smbc1,123,r0c8,r0c6,col1,r0c7,r0c2,r0c3
123,r2c5,is2,ric1,smbc1,123,r2c8,r2c6,col1,r2c7,r2c2,r2c3
234,r3c5,is3,ric3,smbc2,234,r3c8,r3c6,col3,r3c7,r3c2,r3c3
345,r4c5,is4,ric4,smbc2,N/A,r4c8,r4c6,N/A,r4c7,r4c2,r4c3
345,r4c5,is4,N/A,smbc2,N/A,r4c8,r4c6,col5,r4c7,r4c2,r4c3

答案 1 :(得分:1)

我真的很喜欢使用join的答案,但是它确实要求文件按键列进行排序。这是一个没有限制的版本。根据一种理论,即做类似数据库的事情的最佳工具是数据库,该理论将CSV文件导入到临时SQLite数据库的表中,然后对它们运行SELECT以获取所需的输出:

(编辑:基于有关数据的新信息的修订版)

#!/bin/sh
# Usage: ./merge.sh file1.dat file2.dat file3.dat  > output.dat
file1=$1
file2=$2
file3=$3

rm -f scratch.db
sqlite3 -batch -noheader -csv -nullvalue "N/A" scratch.db  <<EOF | perl -pe 's#(?:^|,)\K""(?=,|$)#N/A#g'
CREATE TABLE file1(f1_1 INTEGER, f1_2, f1_3, f1_4, f1_5);
CREATE TABLE file2(f2_1 INTEGER, f2_2);
CREATE TABLE file3(f3_1, f3_2, f3_3, f3_4 INTEGER, f3_5, f3_6, f3_7, f3_8);
.import $file1 file1
.import $file2 file2
.import $file3 file3
-- Build indexes to speed up joining and sorting gigs of data.
CREATE INDEX file1_idx ON file1(f1_1);
CREATE INDEX file2_idx ON file2(f2_1);
CREATE INDEX file3_idx ON file3(f3_4);
SELECT f3_4, f3_5, f1_2, f1_3, f1_5, f2_2, f3_8, f3_6, f1_4, f3_7, f3_1
     , f3_2, f3_3
FROM file3
LEFT JOIN file1 ON f1_1 = f3_4
LEFT JOIN file2 ON f2_1 = f3_4
ORDER BY f3_4;
EOF
rm -f scratch.db

注意:这将使用一个临时数据库文件,该文件将是您所有数据的大小,然后是一些因为索引的大小。如果您受到空间的限制,考虑到连接列 是按数字排序的信息,那么我有一个不使用临时文件的想法,但这是足够的工作,除非被询问,否则我不会打扰