Использование grep с файлом шаблона: печатать одиночные и повторяющиеся записи - PullRequest
4 голосов
/ 26 марта 2012

Позвольте мне начать с того, что я не хочу печатать только дубликаты строк и не хочу их удалять.

Я пытаюсь использовать grep с файлом шаблона для анализа большого файла данных.

Файл шаблона, например, может выглядеть так:

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 ]

2 голосов
/ 27 марта 2012

Одно решение, не с grep, а с perl:

С patternfile.txt и inputfile.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 голос
/ 25 сентября 2013

Вы не столько grep для шаблонов, сколько для левостороннего соединения данных при вводе с данными в шаблоне.

Вы можете (в основном) сделать это с помощью join, удобной утилиты Unix, с которой я хорошо знаком, поскольку пытался решить проблему, аналогичную вашей.

Однако есть пара небольших отличий.

Первая команда:

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

И объяснение:

  • -a 1 означает также включение неприсоединяемых строк из файла 1 ( Patternfile.txt ). Я добавил это, потому что вы хотели включить «пустые» строки для несопоставимых строк, и это было самое близкое, что я мог получить.
  • -2 2 означает присоединение к полю 2 для файла 2 (Вы можете установить поле для -1 FIELD и -2 FIELD, по умолчанию используется поле 1). Это связано с тем, что ключ, к которому вы присоединяетесь в Inputfile.txt , находится во втором столбце
  • <(sort Patternfile.txt) & mdash; файлы должны быть отсортированы в поле объединения для правильной работы объединения.
  • <(sort -k2,2 Inputfile.txt) & mdash; сортировать входной файл от ключа 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 ""  }'
    
...