使用AWK根据多个条件合并两个文件

时间:2019-01-20 23:36:07

标签: csv awk merge text-processing

我知道这个问题已经被问过几次了。这是一个示例:

Using AWK to merge two files based on multiple columns

如果发生以下匹配,我的目标是打印出file_b的第2、4、5和7列以及file_a的第17和18列: file_a.csv的第2、6和7列分别与file_b.csv的第2、4和5列匹配。

但是无论我尝试多少,我都无法使它适合我的情况。这是我的两个文件:

file_a.csv

col2, col6, col7, col17, col18
a, b, c, 145, 88
e, f, g, 101, 96
x, y, z, 243, 222

file_b.csv

col2, col4, col5, col7
a, b, c, 4.5
e, f, g, 6.3
x, k, l, 12.9

输出应如下所示:

col2, col4, col5, col7, col17, col18
a, b, c, 4.5, 145, 88
e, f, g, 6.3, 101, 96

我尝试过:

awk -F, -v RS='\r\n' 'NR==FNR{key[$2 FS $6 FS $7]=$17 FS $18;next} {if($2 FS $4 FS $5 in key); print $2 FS $4 FS $5 FS $7 FS key[$2 FS $6 FS $7]}' file_a.csv file_b.csv > out.csv

当前我得到的输出是:

col2, col4, col5, col7,
a, b, c, 4.5,
e, f, g, 6.3,

换句话说,file_a中的col17和col18没有显示。

昨天我问了一个相关问题,我在哪里遇到换行符问题。答案得到解决,但现在我认为这个问题与检查if条件有关。

更新: 我正在共享指向实际数据的截断副本的链接。这些文件与实际文件之间的唯一区别是,实际文件具有数百万行。这些每个只有10个。

file_a.csv

file_b.csv

3 个答案:

答案 0 :(得分:1)

请尝试以下操作(GNU sed):

awk 'BEGIN{RS="\r\n";FS=OFS=",";SUBSEP=FS}NR==FNR{arr[$2,$6,$7]=$17 FS $18;next} {if(arr[$2,$4,$5]) print $2,$4,$5,$7,arr[$2,$4,$5]}'

这是BEGIN块踢入的时间。OFS也踢入踢的时间。
当我们打印出许多由相同内容分隔开的字段时,我们可以设置OFS,并在要打印的内容之间放入逗号。

在为数组中的键分配值时,无需检查key in arr
默认情况下,如果之前未分配arr[somekey],则其为empty / "",并且其awk的值为false(在标量上下文中为0),并且非空字符串的计算结果为truetrue中没有字面上的falseawk)。
(您使用了错误的array名称,$2,$6,$7是此处数组arr中的键。使用key作为数组名称会造成混淆。)

您可以测试一些简单的概念,例如:

awk 'BEGIN{print arr["newkey"]}'

您不需要输入文件即可执行BEGIN块。

此外,有时可以使用引号,以避免造成混淆和潜在的问题。

更新: 您的文件实际上以\n结尾,如果您不确定行的结尾是什么,请使用以下命令:

awk 'BEGIN{RS="\r\n|\n|\r";FS=OFS=",";SUBSEP=FS}NR==FNR{arr[$2,$6,$7]=$17 FS $18;next} {if(arr[$2,$4,$5]) print $2,$4,$5,$7,arr[$2,$4,$5]}' file_a.csv file_b.csv

或这个(这个将忽略空行):

awk 'BEGIN{RS="[\r\n]+";FS=OFS=",";SUBSEP=FS}NR==FNR{arr[$2,$6,$7]=$17 FS $18;next} {if(arr[$2,$4,$5]) print $2,$4,$5,$7,arr[$2,$4,$5]}' file_a.csv file_b.csv

,最好通过以下方式先进行转换以避免这种情况:

sed -i 's/\r//' files

或者您可以使用dos2unix命令:

dos2unix file

这是一个方便的命令行工具,只能做上述事情。
如果您的系统中还没有它,可以安装它。
转换后,通常情况下无需分配RS

答案 1 :(得分:0)

$ awk 'BEGIN      {RS="\r\n"; FS=OFS=","}
       NR==FNR    {a[$2,$6,$7]=$17 OFS $18; next} 
  ($2,$4,$5) in a {print $2,$4,$5,$7,a[$2,$4,$5]}' file1 file2 > output

您的主要问题是,在数组查找中,您应使用的索引是第二个文件密钥,而不是第一个文件密钥。 if条件之后的分号也是错误的。剩下的只是化妆品。

不确定是否要终止输出\r\n,如果也设置ORS=RS,则仅终止于换行符。

答案 2 :(得分:0)

由于您已经提到该文件很大,因此可以尝试使用Perl。

假定文件具有“ \ r”。

$ cat file_a.csv
col2, col6, col7, col17, col18
a, b, c, 145, 88
e, f, g, 101, 96
x, y, z, 243, 222
$ cat file_b.csv
col2, col4, col5, col7
a, b, c, 4.5
e, f, g, 6.3
x, k, l, 12.9
$ perl -F, -lane 'BEGIN { %kv=map{chomp;chop;@a=split(",");"$a[0],$a[1],$a[2]"=>"$a[3]"} qx(cat file_b.csv) } if($.>1){ $x="$F[0],$F[1],$F[2]";chomp($F[-1]);print "$x,$kv{$x}",join(",",@F[-2,-1]) if $kv{$x} } ' file_a.csv
a, b, c, 4.5 145, 88
e, f, g, 6.3 101, 96
$