根据给定的ID列表从文本文件中提取所有行

时间:2012-12-05 20:57:15

标签: perl unix awk

我有2个文本文件。 file1包含ID列表:

11002
10995
48981
79600

file2

10993   item    0
11002   item    6
10995   item    7
79600   item    7
439481  item    5
272557  item    7
224325  item    7
84156   item    6
572546  item    7
693661  item    7
.....

我正在尝试从file2中选择ID(第一列)位于file1的所有行。目前,我正在做的是遍历第一个文件来创建一个正则表达式:

^\b11002\b\|^\b10995\b\|^\b48981\b|^\b79600\b

然后运行:

grep '^11002\|^10995\|^48981|^79600' file2.txt

但是当file1中的ID数量太大(~2000)时,正则表达式变得很长而grep变慢。还有另外一种方法吗?我使用的是Perl + Awk + ​​Unix。

7 个答案:

答案 0 :(得分:5)

使用hash table。它可能是内存密集型的,但查找是在恒定的时间内。这是一个高效且正确的过程 - 不是唯一的,但有效且正确 - 用于创建哈希表,使用file1作为键,使用file2查找哈希表中的键。如果键位于哈希表中,则该行将打印到标准输出:

#!/usr/bin/env perl

use strict;
use warnings;

open FILE1, "< file1" or die "could not open file1\n";
my $keyRef;
while (<FILE1>) {
   chomp;
   $keyRef->{$_} = 1;
}
close FILE1;

open FILE2, "< file2" or die "could not open file2\n";
while (<FILE2>) {
    chomp;
    my ($testKey, $label, $count) = split("\t", $_);
    if (defined $keyRef->{$testKey}) {
        print STDOUT "$_\n";
    }
}
close FILE2;

在Perl中有很多方法可以做同样的事情。也就是说,我重视清晰度和明确性,而不是花哨的默默无闻,因为你永远不知道什么时候你必须回到Perl脚本并进行更改,而且它们很难管理,就像它一样。一个人的意见。

答案 1 :(得分:4)

awk 'NR==FNR{tgts[$1]; next} $1 in tgts' file1 file2

查找

$ cat file1
11002
10995
48981
79600
$ cat file2
10993   item    0
11002   item    6
10995   item    7
79600   item    7
439481  item    5
272557  item    7
224325  item    7
84156   item    6
572546  item    7
693661  item    7
$ awk 'NR==FNR{tgts[$1]; next} $1 in tgts' file1 file2
11002   item    6
10995   item    7
79600   item    7

答案 2 :(得分:2)

使用grep

$ grep -f f1 f2
11002   item    6
10995   item    7
79600   item    7

注意:我在多个系统上测试了很多建议的答案,有些只显示最后一个匹配79600 item 7

答案 3 :(得分:2)

我建议使用专门设计的工具。使用join命令。 “男人加入”了解更多信息。

linux_prompt> join file1 file2
11002 item 6
10995 item 7
79600 item 7

答案 4 :(得分:1)

将第一个文件的所有元素加载到哈希中。 对于第二个文件的每一行,   使用正则表达式^(\d*)提取数字   如果哈希包含提取的数字,则将其打印

答案 5 :(得分:0)

使用进程替换将file1中的ID转换为正则表达式:

grep -f <(sed 's/.*/^&\\b/' file1) file2

我假设您正在使用bash或类似功能的shell

答案 6 :(得分:0)

简单的perl解决方案是使用散列并计算所追求的数字的出现次数。

perl -lanwe 'print if $a{$F[0]}++ == 1;' file1.txt file2.txt

我从您的示例数据中获得以下输出:

11002   item    6
10995   item    7
79600   item    7

请注意,您需要在命令行上以正确的顺序使用这些文件。

这将打开并读取输入文件名(-n),将行(-a)自动分割为@F,然后打印每一行,如果散列中的值为该数字等于1.如果要从file2打印多个值,只需将== 1更改为>= 1

请注意,在完成相等性比较后应用++运算符。