我使用的是 Solaris 10,因此涉及 -f 的 grep 选项不起作用。
我有两个用管道分隔的文件:
文件1:
abc|123|BNY|apple|
cab|234|cyx|orange|
def|kumar|pki|bird|
文件2:
abc|123|
kumar|pki|
cab|234
我想将 file2 的前两列与 file1 进行比较(搜索前两列中 file1 的全部内容),如果它们匹配,则打印 file1 的匹配行。然后搜索文件2的第二行,依此类推。
预期输出:
abc|123|BNY|apple|
cab|234|cyx|orange|
我的文件很大,包含大约 400,000 行,所以我想让执行速度更快。
答案1
这就是 awk 的设计目的:
$ awk -F'|' 'NR==FNR{c[$1$2]++;next};c[$1$2] > 0' file2 file1
abc|123|BNY|apple|
cab|234|cyx|orange|
解释
-F'|'
:将字段分隔符设置为|
。NR==FNR
:NR 是当前输入行号,FNR 是当前文件的行号。仅当读取第一个文件时,两者才会相等。c[$1$2]++; next
:如果这是第一个文件,则将前两个字段保存在c
数组中。然后,跳到下一行,以便仅应用于第一个文件。c[$1$2]>0
:只有当这是第二个文件时,else 块才会被执行,因此我们检查该文件的字段 1 和 2 是否已经被看到 (c[$1$2]>0
),如果已经被看到,我们打印该行。在 中awk
,默认操作是打印该行,因此如果c[$1$2]>0
为 true,则将打印该行。
或者,由于您使用 Perl 标记:
perl -e 'open(A, "file2"); while(<A>){/.+?\|[^|]+/ && $k{$&}++};
while(<>){/.+?\|[^|]+/ && do{print if defined($k{$&})}}' file1
解释
第一行将打开,读取第二行( )file2
之前的所有内容,并将其(这是最后一个匹配运算符的结果)保存在哈希中。|
.+?\|[^|]+
$&
%k
第二行处理 file1,使用相同的正则表达式提取前两列并打印该行(如果这些列在哈希中定义)%k
。
上述两种方法都需要将 file2 的前 2 列保存在内存中。如果你只有几十万行,那应该不是问题,但如果是的话,你可以这样做
cut -d'|' -f 1,2 file2 | while read pat; do grep "^$pat" file1; done
但这会更慢。
答案2
我认为
grep -Ff file2 file1
这就是您正在寻找的。它应该是有效的,但我不确定它会像你想要的那样准确。如果abc|123
(例如)在file1
不同列的一行中找到,则该行也将被打印。如果你能保证这种情况永远不会发生,那么上面的代码应该可以工作。
答案3
如果你想以 SQL 的方式思考问题,那么你绝对应该尝试一个名为 'q':
$ q -d '|' "select f1.* from file1 f1 join file2 f2 on (f1.c1 = f2.c1 and f1.c2 = f2.c2)"
如果熟悉SQL查询的话会更加清晰易懂。
答案4
$ sed 's/^/\^/' 2.txt > temp.txt ; grep 1.txt -f temp.txt
abc|123|BNY|apple|
cab|234|cyx|orange|