如何比较两个不同文件中的两对列并打印水平合并的匹配项(首选 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. 将两个文件中的第 1 列和第 2 列提取到单独的列中
  2. 加入文件。

The following works on repl:

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

使用 sortpasteGNU 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'