将grep与模式文件一起使用:打印单个和重复的条目

时间:2012-03-26 19:44:51

标签: grep duplicate-data line-breaks no-match

首先让我说我不想只打印重复的行,也不想删除它们。

我正在尝试将grep与模式文件一起使用来解析大型数据文件。

例如,Pattern文件可能如下所示:

1243
1234
1234
1234
1354
1356
1356
1677

等。包含更多单个和重复条目。

输入数据文件可能如下所示:

aatta   1243    qqqqqq
yyyyy   1234    vvvvvv
ttttt   1555    bbbbbb
ppppp   1354    pppppp
yyyyy   3333    zzzzzz
qqqqq   1677    eeeeee
iiiii   4444    iiiiii

等。为27000行。

当我使用

grep -f 'Patternfile.txt' 'Inputfile.txt' > 'Outputfile.txt'

我得到一个类似于此的输出文件:

aatta   1243    qqqqqq
yyyyy   1234    vvvvvv
ppppp   1354    pppppp

我怎么能得到它来报告重复项,所以我最终得到这样的东西?:

aatta   1243    qqqqqq
yyyyy   1234    vvvvvv
yyyyy   1234    vvvvvv
yyyyy   1234    vvvvvv
ppppp   1354    pppppp


qqqqq   1677    zzzzzz

此外,如果模式文件中的查询与输入文件中的子字符串不匹配,我还想打印一个空白行。

谢谢!

2 个答案:

答案 0 :(得分:2)

一种解决方案,不是grep,而是perl

patternfile.txtinputfile.txt包含原始帖子的数据。 script.pl的下一个内容应该完成这项工作(我假设要匹配的字符串是第二列,否则应该修改为使用regexp。这种方式更快):

use warnings;
use strict;

## Check arguments.
die qq[Usage: perl $0 <pattern-file> <input-file>\n] unless @ARGV == 2;

## Open input files.
open my $pattern_fh, qq[<], shift @ARGV or die qq[Cannot open pattern file\n];
open my $input_fh, qq[<], shift @ARGV or die qq[Cannot open input file\n];

## Hash to save patterns.
my (%pattern, %input);

## Read each pattern and save how many times appear in the file.
while ( <$pattern_fh> ) { 
    chomp;
    if ( exists $pattern{ $_ } ) { 
        $pattern{ $_ }->[1]++;
    }   
    else {
        $pattern{ $_ } = [ $., 1 ];
    }   
}

## Read file with data and save them in another hash.
while ( <$input_fh> ) { 
    chomp;
    my @f = split;
    $input{ $f[1] } = $_; 
}

## For each pattern, search it in the data file. If it appears, print line those
## many times saved previously, otherwise print a blank line.
for my $p ( sort { $pattern{ $a }->[0] <=> $pattern{ $b }->[0] } keys %pattern ) { 
    if ( $input{ $p } ) { 
        printf qq[%s\n], $input{ $p } for ( 1 .. $pattern{ $p }->[1] );
    }   
    else {
         # Old behaviour.
         # printf qq[\n];

         # New requirement.
         printf qq[\n] for ( 1 .. $pattern{ $p }->[1] );
    }   
}

像以下一样运行:

perl script.pl patternfile.txt inputfile.txt

并给出下一个输出:

aatta   1243    qqqqqq
yyyyy   1234    vvvvvv
yyyyy   1234    vvvvvv
yyyyy   1234    vvvvvv
ppppp   1354    pppppp


qqqqq   1677    eeeeee

答案 1 :(得分:1)

对于模式而言,你并没有grep这么多 - 将输入中的数据加入到模式中的数据中。

你可以(大部分)用join完成这个,这是一个方便的Unix工具,我已经很好地了解了,因为我一直试图解决类似你的问题。

但是有一些小的差异。

首先命令:

join -a 1 -2 2 <(sort Patternfile.txt) <(sort -k2,3 Inputfile.txt)

并解释:

  • -a 1表示还包含文件1中的不可加入的行( Patternfile.txt )。我添加了这个,因为你想要包含&#34;空白&#34;无法匹配行的行,这是我能得到的最接近的行。
  • -2 2表示在字段2上加入文件2(您可以为-1 FIELD-2 FIELD设置字段,默认为字段1)。这是因为您在 Inputfile.txt 中加入的密钥位于第二列
  • <(sort Patternfile.txt) - 必须在连接字段中对文件进行排序才能使连接正常工作。
  • <(sort -k2,2 Inputfile.txt) - 将输入文件从键2排序到键2,包括

<强>输出:

1234 yyyyy vvvvvv
1234 yyyyy vvvvvv
1234 yyyyy vvvvvv
1243 aatta qqqqqq
1354 ppppp pppppp
1356
1356
1677 qqqqq eeeeee

<强>差异

指定输出与此结果之间的差异很小:

  • 按键顺序排序。
  • 无法加入的行仍包含其原始密钥。如果这是一个问题,您可以通过简单的awk管道来清除不匹配的行:

    ... | awk '{ if ($2 != "") print; else print ""  }'