Perl 脚本使用另一个文件从一个管道分隔的文件中删除行,该文件有一个匹配的列

Perl 脚本使用另一个文件从一个管道分隔的文件中删除行,该文件有一个匹配的列

我有两个巨大的文件,File A其中包含以管道 ( |) 分隔的列的所有记录,并且File B其中只有一列。

我需要删除File A该一列值与 . 匹配的所有行File B

我希望根据文件 A 中的第 2 列检查文件 B 中的第 1 列

文件 B - 文件 B 中的唯一列

818815504
842019301
880702511

文件 A - 文件 A 中的第二列

2020-01-19|777504559|2|15|1|403941|53|4708267|3036|5033|127380343|0|3905585|C|1168|IRU|107|NR
2020-01-19|818815504|2|15|1|403933|8|2394063|3036|2394067|4076948618|0|15177438|B|1168|CRU|98|NK
2020-01-19|842019301|2|15|1|2|2|3712|3036|961249|65707843|65707843|8591573|B|1168|IRU|1|NJ
2020-01-19|847082255|2|15|1|2|2|284|3036|291|125650195|125650195|8870299|C|1168|IRU|1|NR
2020-01-19|858760375|2|15|1|403941|53|4708267|3036|306|99931425|0|8866849|C|1168|IRU|107|NR
2020-01-19|869323039|2|15|1|2|2|371|3036|307|106104029|106104029|4518435|C|1168|IRU|2|NR
2020-01-19|872268371|2|15|1|403941|53|4708267|3036|143|127382679|0|8866849|C|1168|IRU|107|NR
2020-01-19|858760375|2|15|1|2|2|117|3036|109|2739523|5208959|4037061|C|1168|IRU|2|NR

答案1

假设结果的顺序不重要:

$ join -v 1 -t '|' -1 2 <( sort -t '|' -k2,2 fileA ) <( sort fileB )
777504559|2020-01-19|2|15|1|403941|53|4708267|3036|5033|127380343|0|3905585|C|1168|IRU|107|NR
847082255|2020-01-19|2|15|1|2|2|284|3036|291|125650195|125650195|8870299|C|1168|IRU|1|NR
858760375|2020-01-19|2|15|1|2|2|117|3036|109|2739523|5208959|4037061|C|1168|IRU|2|NR
858760375|2020-01-19|2|15|1|403941|53|4708267|3036|306|99931425|0|8866849|C|1168|IRU|107|NR
869323039|2020-01-19|2|15|1|2|2|371|3036|307|106104029|106104029|4518435|C|1168|IRU|2|NR
872268371|2020-01-19|2|15|1|403941|53|4708267|3036|143|127382679|0|8866849|C|1168|IRU|107|NR

join实用程序对给定的两个数据集执行关系 INNER JOIN 操作。数据集必须在我们加入的列上排序,这就是我们在调用之前对文件进行排序的原因join(如果您的 shell 不支持进程替换,请单独对文件进行预排序)。我们在join这里使用的选项-t '|'-1 2表示“用作|列分隔符,并连接到第一个数据集的第二列”(否则第一列是默认值)。

我们-v 1请求第一个文件中无法与第二个文件中的行配对的所有行。

输出将始终首先列出连接列,因此我们可能希望通过使用以下命令交换前两列来将其移动到正确的位置awk

$ join -v 1 -t '|' -1 2 <( sort -t '|' -k2,2 fileA ) <( sort fileB ) | awk 'BEGIN { FS=OFS="|" } { t=$1; $1=$2; $2=t; print }'
2020-01-19|777504559|2|15|1|403941|53|4708267|3036|5033|127380343|0|3905585|C|1168|IRU|107|NR
2020-01-19|847082255|2|15|1|2|2|284|3036|291|125650195|125650195|8870299|C|1168|IRU|1|NR
2020-01-19|858760375|2|15|1|2|2|117|3036|109|2739523|5208959|4037061|C|1168|IRU|2|NR
2020-01-19|858760375|2|15|1|403941|53|4708267|3036|306|99931425|0|8866849|C|1168|IRU|107|NR
2020-01-19|869323039|2|15|1|2|2|371|3036|307|106104029|106104029|4518435|C|1168|IRU|2|NR
2020-01-19|872268371|2|15|1|403941|53|4708267|3036|143|127382679|0|8866849|C|1168|IRU|107|NR

仅使用awk

$ awk -F '|' 'FNR==NR { key[$1]=1; next } !($2 in key)' fileB fileA
2020-01-19|777504559|2|15|1|403941|53|4708267|3036|5033|127380343|0|3905585|C|1168|IRU|107|NR
2020-01-19|847082255|2|15|1|2|2|284|3036|291|125650195|125650195|8870299|C|1168|IRU|1|NR
2020-01-19|858760375|2|15|1|403941|53|4708267|3036|306|99931425|0|8866849|C|1168|IRU|107|NR
2020-01-19|869323039|2|15|1|2|2|371|3036|307|106104029|106104029|4518435|C|1168|IRU|2|NR
2020-01-19|872268371|2|15|1|403941|53|4708267|3036|143|127382679|0|8866849|C|1168|IRU|107|NR
2020-01-19|858760375|2|15|1|2|2|117|3036|109|2739523|5208959|4037061|C|1168|IRU|2|NR

首先读取我们想要提取到关联数组中的数字key。然后,当解析第二个文件时,如果第二个字段不是该数组中的键,则打印该行。

这将按行在该文件中出现的顺序输出fileA,但缺点是所有数字都fileB需要保存在内存中。


根据要求使用 Perl:

$ perl -F'\|' -le 'if (!$flag){ $key{$F[0]}=1 } elsif ($key{$F[1]}!=1) { print $_ }; if (!$flag && eof) { $flag=1 };' fileB fileA
2020-01-19|777504559|2|15|1|403941|53|4708267|3036|5033|127380343|0|3905585|C|1168|IRU|107|NR
2020-01-19|847082255|2|15|1|2|2|284|3036|291|125650195|125650195|8870299|C|1168|IRU|1|NR
2020-01-19|858760375|2|15|1|403941|53|4708267|3036|306|99931425|0|8866849|C|1168|IRU|107|NR
2020-01-19|869323039|2|15|1|2|2|371|3036|307|106104029|106104029|4518435|C|1168|IRU|2|NR
2020-01-19|872268371|2|15|1|403941|53|4708267|3036|143|127382679|0|8866849|C|1168|IRU|107|NR
2020-01-19|858760375|2|15|1|2|2|117|3036|109|2739523|5208959|4037061|C|1168|IRU|2|NR

这模仿了awk程序,但由于 Perl 没有单独的NR内置FNR变量,因此我们需要维护一些状态 ( $flag) 来告诉我们当前是否正在解析第一个文件或第二个文件。

相关内容