根据特定字段在两个不同文件中查找唯一行

根据特定字段在两个不同文件中查找唯一行

我需要比较两个包含多行的 txt 文件,并创建第三个 txt 文件,其中仅包含文件 1 特有的行。文件 1 的示例如下:

../../A/folder/fname.gz | -12.36 | A:BCD:123:A, D:DFR:241:AZ1 
../../A/folder/fname2.gz | -4.56 | B:ABC:456:C | G:RFT:265:T

它持续了数千行,而文件 2 的示例可能是:

../../B/folder2/fname.gz | -7.65 | C:ABC:425:A
../../B/folder2/fname3.gz | -12.31 | A:BCD:758:D

我需要从文件 1 中获取基于第一个字段唯一的所有行(例如../../folder/fname2.gz在示例中)。请注意folder,可以不同,但fnameX.gz​​应该是唯一的。folder和都fname包含-和/或_。每行中的字段数可以不同。上面示例的预期输出应该是:

../../A/folder/fname2.gz | -4.56 | B:ABC:456:C | G:RFT:265:T

做这个的最好方式是什么?

答案1

awk -F ' *[|] *' '{ k=$1; sub(".*/", "", k) }
               !z { a[k]; next } !( k in a )' file2 z=1 file1

首先读取file2,将文件名部分存储在数组中。读取时file1,如果数组中不存在文件名,则打印一行。

答案2

我们可以使用一个施瓦茨变换和一个辅助 shell 函数,并假设每个文件的第一个字段中的文件名不包含|字符,并且字段由|可选空格(空格和/或制表符)包围。

我们首先定义一个辅助函数 ,sorter它从输入数据的第一个字段中提取文件名,并在对数据进行排序之前在每一行中添加此文件名前缀:

sorter () {
    awk -F '[[:blank:]]*\|[[:blank:]]*' -v OFS='|' \
        '{ key=$1; sub(".*/","",key); print key, $0 }' |
    sort
}

$0如果您只需要最终输出中第一个字段的路径名,请更改$1上面代码中的 。将其更改$2为仅获取第二个字段等。

数据需要按文件名排序,因为join实用程序假定其输入是按连接字段排序的。

该函数从其标准输入中读取数据,如果我们只通过它运行第一个文件,则结果如下:

$ sorter <file1
fname.gz|../../A/folder/fname.gz | -12.36 | A:BCD:123:A, D:DFR:241:AZ1
fname2.gz|../../A/folder/fname2.gz | -4.56 | B:ABC:456:C | G:RFT:265:T

在每个输入文件上执行此操作并使用join连接第一个字段,我们可能会要求join仅输出第一个文件中无法与第二个文件中的任何行配对的行,使用join -v 1

join -t '|' -v 1 <( sorter <file1 ) <( sorter <file2 ) | cut -d '|' -f 2-

最后,该命令删除我们的命令在每行添加的cut文件名字段。awk

考虑到问题中的数据,结果将是

../../A/folder/fname2.gz | -4.56 | B:ABC:456:C | G:RFT:265:T

在代码中$0替换为,你会得到$1awk

../../A/folder/fname2.gz

相关内容