使用AWK根据多个条件合并两个文件
Using AWK to merge two files based on multiple conditions
我知道这个问题已经被问过好几次了。这是一个例子:
我的目标是打印出 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[ FS FS ]= FS ;next} {if( FS FS in key); print FS FS FS FS key[ FS FS ]}' 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 个。
请试试这个 (GNU sed):
awk 'BEGIN{RS="\r\n";FS=OFS=",";SUBSEP=FS}NR==FNR{arr[,,]= FS ;next} {if(arr[,,]) print ,,,,arr[,,]}'
这是 BEGIN
块开始的时间。同时 OFS
开始。
当我们要打印出很多由同一个东西分隔的字段时,我们可以设置OFS
,并简单地在我们要打印的东西之间加上逗号。
为数组中的键赋值后,无需检查 key in arr
,
默认情况下,当之前未分配 arr[somekey]
时,它是 empty
/""
,并且它在 awk 中计算为 false
(在标量上下文中为 0
),并且非空字符串的计算结果为 true
(awk
中没有字面上的 true
和 false
)。
(你使用了错误的array
名称,,,
是数组arr
中的键。使用key
作为数组名称会造成混淆。)
您可以像这样测试一些简单的概念:
awk 'BEGIN{print arr["newkey"]}'
您不需要输入文件来执行 BEGIN
块。
此外,有时您可以使用引号,以避免混淆和潜在问题。
更新:
您的文件实际上以 \n
结尾,如果您不能确定行结尾是什么,请使用:
awk 'BEGIN{RS="\r\n|\n|\r";FS=OFS=",";SUBSEP=FS}NR==FNR{arr[,,]= FS ;next} {if(arr[,,]) print ,,,,arr[,,]}' file_a.csv file_b.csv
或者这个(这个会忽略空行):
awk 'BEGIN{RS="[\r\n]+";FS=OFS=",";SUBSEP=FS}NR==FNR{arr[,,]= FS ;next} {if(arr[,,]) print ,,,,arr[,,]}' file_a.csv file_b.csv
还有,最好先转换,避免这种情况,by:
sed -i 's/\r//' files
或者你可以使用dos2unix
命令:
dos2unix file
这是一个方便的命令行工具,只做上面的事情。
如果您的系统中没有它,您可以安装它。
转换后,一般情况下不需要赋值RS
。
$ awk 'BEGIN {RS="\r\n"; FS=OFS=","}
NR==FNR {a[,,]= OFS ; next}
(,,) in a {print ,,,,a[,,]}' file1 file2 > output
您的主要问题是,在数组查找中,您应该使用的索引是第二个文件键,而不是第一个文件键。 if 条件后的分号也是错误的。其余的只是化妆品。
不确定您是否希望输出 \r\n
终止,如果是这样也设置 ORS=RS
,否则它只是换行符。
既然你提到文件很大,你可以尝试一下 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
$
我知道这个问题已经被问过好几次了。这是一个例子:
我的目标是打印出 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[ FS FS ]= FS ;next} {if( FS FS in key); print FS FS FS FS key[ FS FS ]}' 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 个。
请试试这个 (GNU sed):
awk 'BEGIN{RS="\r\n";FS=OFS=",";SUBSEP=FS}NR==FNR{arr[,,]= FS ;next} {if(arr[,,]) print ,,,,arr[,,]}'
这是 BEGIN
块开始的时间。同时 OFS
开始。
当我们要打印出很多由同一个东西分隔的字段时,我们可以设置OFS
,并简单地在我们要打印的东西之间加上逗号。
为数组中的键赋值后,无需检查 key in arr
,
默认情况下,当之前未分配 arr[somekey]
时,它是 empty
/""
,并且它在 awk 中计算为 false
(在标量上下文中为 0
),并且非空字符串的计算结果为 true
(awk
中没有字面上的 true
和 false
)。
(你使用了错误的array
名称,,,
是数组arr
中的键。使用key
作为数组名称会造成混淆。)
您可以像这样测试一些简单的概念:
awk 'BEGIN{print arr["newkey"]}'
您不需要输入文件来执行 BEGIN
块。
此外,有时您可以使用引号,以避免混淆和潜在问题。
更新:
您的文件实际上以 \n
结尾,如果您不能确定行结尾是什么,请使用:
awk 'BEGIN{RS="\r\n|\n|\r";FS=OFS=",";SUBSEP=FS}NR==FNR{arr[,,]= FS ;next} {if(arr[,,]) print ,,,,arr[,,]}' file_a.csv file_b.csv
或者这个(这个会忽略空行):
awk 'BEGIN{RS="[\r\n]+";FS=OFS=",";SUBSEP=FS}NR==FNR{arr[,,]= FS ;next} {if(arr[,,]) print ,,,,arr[,,]}' file_a.csv file_b.csv
还有,最好先转换,避免这种情况,by:
sed -i 's/\r//' files
或者你可以使用dos2unix
命令:
dos2unix file
这是一个方便的命令行工具,只做上面的事情。
如果您的系统中没有它,您可以安装它。
转换后,一般情况下不需要赋值RS
。
$ awk 'BEGIN {RS="\r\n"; FS=OFS=","}
NR==FNR {a[,,]= OFS ; next}
(,,) in a {print ,,,,a[,,]}' file1 file2 > output
您的主要问题是,在数组查找中,您应该使用的索引是第二个文件键,而不是第一个文件键。 if 条件后的分号也是错误的。其余的只是化妆品。
不确定您是否希望输出 \r\n
终止,如果是这样也设置 ORS=RS
,否则它只是换行符。
既然你提到文件很大,你可以尝试一下 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
$