如何比较两个不同文件中的两对列并打印水平合并的匹配项(首选 BASH 或 AWK)?
How to compare two pairs of columns in two different files and print matches merged horizontally (preference for BASH or AWK)?
我试图通过匹配两对不同的列来比较两个制表符分隔的文件。匹配后,我想打印水平合并的两个文件中的匹配行(即并排,类似于 BASH 粘贴)。这些文件有 12 列。
到处找都没有发现类似的问题,老实说,我担心我可能想多了。
简单地说,目标是“将第 1 列(在文件 1 中)与第 2 列(在文件 2 中)匹配”和“第 2 列(在文件 1 中)与第 1 列(在文件 2 中)”和然后才并排打印相应的匹配行。
一个例子:
File1.tsv
1 A ExtraInfo
2 B ExtraInfo
3 C ExtraInfo
4 D ExtraInfo
5 E ExtraInfo
File2.tsv
D 4 ExtraInfo
B 7 ExtraInfo
E 9 ExtraInfo
C 3 ExtraInfo
A 1 ExtraInfo
期望的输出:
1 A ExtraInfo A 1 ExtraInfo
3 C ExtraInfo C 3 ExtraInfo
4 D ExtraInfo D 4 ExtraInfo
虽然我找不到遇到相同问题的人,但我确实发现了一些类似的问题,这让我认为 awk 可能是我最好的选择。不幸的是,我仍然对它一窍不通,所以我的尝试仅限于尝试从类似问题中改编代码。
我尝试了以下方法,但无济于事:
awk 'NR==FNR{a[]= && a[]=;next} {print [=14=],a[[=14=]]}' file1 file2
尽管如此,如果有人只能用另一种语言默认为 Ubuntu 提供帮助,我仍然会很感激。
- 将两个文件中的第 1 列和第 2 列提取到单独的列中
- 加入文件。
join -t$'\t' -14 -24 -o 1.1,1.2,1.3,2.1,2.2,2.3 <(
awk -v F='\t' -v OFS='\t' '{NF++;=}1' File1.tsv |
sort -k4) <(
awk -v F='\t' -v OFS='\t' '{NF++;=}1' File2.tsv |
sort -k4)
如果要保留第一个文件的排序顺序,请使用 nl
对第一个文件中的行进行编号,进行连接,然后使用 sort -k1
对行号重新排序并删除带有 cut
.
的行号
注意:在 OP 提供了更多详细信息后(例如,文件有 12 列),我在我的答案中添加了@karafka 的建议编辑。
假设:
- 每个第一/第二列对在一个文件中都是唯一的(即,每个文件最多匹配一行)
- 第 3 列不包含任何制表符(即每个制表符分隔的文件共有 3 列)
- 输出按第一列和第二列排序(即,不尝试根据源文件的内容维护任何排序)
一个awk/sort
解法:
awk -F"\t" ' # input delimiter is a tab
BEGIN { OFS=FS } # output delimiter is also a tab
NR==FNR { a[,]=[=10=] ; next } # store first file line in array using fields 1 & 2 as index
(,) in a { print a[,],[=10=] } # if array entry exists with first 2 fields as index (in reverse order) then print array element==matching-line-from-file1 and [=10=]==current-line-from-file2 to stdout
' file1.tsv file2.tsv | sort # sort output from awk [optional]
注意:删除注释以整理代码。
运行 以上示例数据文件生成:
1 A ExtraInfo A 1 ExtraInfo
3 C ExtraInfo C 3 ExtraInfo
4 D ExtraInfo D 4 ExtraInfo
使用 sort
、paste
和 GNU egrep
:
paste File1.tsv <(sort File2.tsv) |
egrep '^(\w)\W*(\w)\W*(\w*\W*){1}\W*\W'
输出:
1 A ExtraInfo A 1 ExtraInfo
3 C ExtraInfo C 3 ExtraInfo
4 D ExtraInfo D 4 ExtraInfo
工作原理:
注意输出是怎样的(至少对于匹配的项目),一个部分回文的列表——但是要省略的行是不是回文。
首先sort
未排序的File2.tsv,然后paste
两个文件放在一起。
GNU grep
提供支持搜索回文字符串的反向表达式。
如果有更多列,请将 {1}
更改为有多少 extra 列。因此,如果每个 .tsv 文件中有 12 列,请将 {1}
更改为 {10}
,如下所示:
paste File1.tsv <(sort File2.tsv) |
egrep '^(\w)\W*(\w)\W*(\w*\W*){10}\W*\W'
我试图通过匹配两对不同的列来比较两个制表符分隔的文件。匹配后,我想打印水平合并的两个文件中的匹配行(即并排,类似于 BASH 粘贴)。这些文件有 12 列。
到处找都没有发现类似的问题,老实说,我担心我可能想多了。
简单地说,目标是“将第 1 列(在文件 1 中)与第 2 列(在文件 2 中)匹配”和“第 2 列(在文件 1 中)与第 1 列(在文件 2 中)”和然后才并排打印相应的匹配行。
一个例子:
File1.tsv
1 A ExtraInfo
2 B ExtraInfo
3 C ExtraInfo
4 D ExtraInfo
5 E ExtraInfo
File2.tsv
D 4 ExtraInfo
B 7 ExtraInfo
E 9 ExtraInfo
C 3 ExtraInfo
A 1 ExtraInfo
期望的输出:
1 A ExtraInfo A 1 ExtraInfo
3 C ExtraInfo C 3 ExtraInfo
4 D ExtraInfo D 4 ExtraInfo
虽然我找不到遇到相同问题的人,但我确实发现了一些类似的问题,这让我认为 awk 可能是我最好的选择。不幸的是,我仍然对它一窍不通,所以我的尝试仅限于尝试从类似问题中改编代码。
我尝试了以下方法,但无济于事:
awk 'NR==FNR{a[]= && a[]=;next} {print [=14=],a[[=14=]]}' file1 file2
尽管如此,如果有人只能用另一种语言默认为 Ubuntu 提供帮助,我仍然会很感激。
- 将两个文件中的第 1 列和第 2 列提取到单独的列中
- 加入文件。
join -t$'\t' -14 -24 -o 1.1,1.2,1.3,2.1,2.2,2.3 <(
awk -v F='\t' -v OFS='\t' '{NF++;=}1' File1.tsv |
sort -k4) <(
awk -v F='\t' -v OFS='\t' '{NF++;=}1' File2.tsv |
sort -k4)
如果要保留第一个文件的排序顺序,请使用 nl
对第一个文件中的行进行编号,进行连接,然后使用 sort -k1
对行号重新排序并删除带有 cut
.
注意:在 OP 提供了更多详细信息后(例如,文件有 12 列),我在我的答案中添加了@karafka 的建议编辑。
假设:
- 每个第一/第二列对在一个文件中都是唯一的(即,每个文件最多匹配一行)
- 第 3 列不包含任何制表符(即每个制表符分隔的文件共有 3 列)
- 输出按第一列和第二列排序(即,不尝试根据源文件的内容维护任何排序)
一个awk/sort
解法:
awk -F"\t" ' # input delimiter is a tab
BEGIN { OFS=FS } # output delimiter is also a tab
NR==FNR { a[,]=[=10=] ; next } # store first file line in array using fields 1 & 2 as index
(,) in a { print a[,],[=10=] } # if array entry exists with first 2 fields as index (in reverse order) then print array element==matching-line-from-file1 and [=10=]==current-line-from-file2 to stdout
' file1.tsv file2.tsv | sort # sort output from awk [optional]
注意:删除注释以整理代码。
运行 以上示例数据文件生成:
1 A ExtraInfo A 1 ExtraInfo
3 C ExtraInfo C 3 ExtraInfo
4 D ExtraInfo D 4 ExtraInfo
使用 sort
、paste
和 GNU egrep
:
paste File1.tsv <(sort File2.tsv) |
egrep '^(\w)\W*(\w)\W*(\w*\W*){1}\W*\W'
输出:
1 A ExtraInfo A 1 ExtraInfo
3 C ExtraInfo C 3 ExtraInfo
4 D ExtraInfo D 4 ExtraInfo
工作原理:
注意输出是怎样的(至少对于匹配的项目),一个部分回文的列表——但是要省略的行是不是回文。
首先sort
未排序的File2.tsv,然后paste
两个文件放在一起。
GNU grep
提供支持搜索回文字符串的反向表达式。
如果有更多列,请将 {1}
更改为有多少 extra 列。因此,如果每个 .tsv 文件中有 12 列,请将 {1}
更改为 {10}
,如下所示:
paste File1.tsv <(sort File2.tsv) |
egrep '^(\w)\W*(\w)\W*(\w*\W*){10}\W*\W'