Grep, awk или sed? Печатать строки в одном файле, сопоставляя шаблоны в другом файле

У меня есть файл с более чем 40 000 строк (file1), и я хочу извлечь строки, соответствующие шаблонам в файле2 (около 6000 строк). Я использую grep, как это, но он очень медленный: grep -f file2 file1 > out

Есть ли более быстрый способ сделать это с помощью awk или sed?

Вот некоторые выдержки из моих файлов:

File1:
scitn003869.2| scign003869 CGCATGTGTGCATGTATTATCGTATCCCTTG
scitn007747.1| scign007747  CACGCAGACGCAGTGGAGCATTCCAGGTCACAA
scitn003155.1| scign003155  TAAAAATCGTTAGCACTCGCTTGGTACACTAAC
scitn018252.1| scign018252  CGTGTGTGTGCATATGTGTGCATGCGTG
scitn004671.2| scign004671  TCCTCAGGTTTTGAAAGGCAGGGTAAGTGCT

File2:
scign000003
scign000004
scign000005
scign004671
scign000013

`

Ответ 1

Попробуйте grep -Fwf file2 file1 > out

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

Ответ 2

Вот как это сделать в awk:

awk 'NR==FNR{pats[$0]; next} $2 in pats' File2 File1

Используя 60 000 строк File1 (ваш File1 повторяется 8000 раз) и 6 000 файлов File2 (ваш повторил 1200 раз):

$ time grep -Fwf File2 File1 > ou2

real    0m0.094s
user    0m0.031s
sys     0m0.062s

$ time awk 'NR==FNR{pats[$0]; next} $2 in pats' File2 File1 > ou1

real    0m0.094s
user    0m0.015s
sys     0m0.077s

$ diff ou1 ou2

то есть. это примерно так же быстро, как grep. Следует отметить, однако, что решение awk позволяет выбрать конкретное поле для соответствия, поэтому, если что-либо из File2 появляется в другом месте в File1, вы не получите ложное совпадение. Он также позволяет вам сопоставлять по всему полю за раз, поэтому, если ваши целевые строки были различной длины, и вы не хотели бы, чтобы "scign000003" соответствовал "scign0000031" (хотя -w для grep дает аналогичную защиту для этого).

Для полноты, здесь время для другого решения awk отправлено:

$ time awk 'BEGIN{i=0}FNR==NR{a[i++]=$1;next}{for(j=0;j<i;j++)if(index($0,a[j]))print $0}' File2 File1 > ou3

real    3m34.110s
user    3m30.850s
sys     0m1.263s

и вот время, которое я получаю для perl script Отметить Mark:

$ time ./go.pl > out2

real    0m0.203s
user    0m0.124s
sys     0m0.062s

Ответ 3

Вы можете попробовать с этим awk:

awk 'BEGIN{i=0}
FNR==NR { a[i++]=$1; next }
{ for(j=0;j<i;j++)
    if(index($0,a[j]))
        {print $0;break}
}' file2 file1

Часть FNR==NR указывает, что материал, следующий за ним в фигурных скобках, применяется только при обработке первого входного файла (file2). И он говорит, чтобы сохранить все слова, которые вы ищете в массиве a[]. Бит во втором наборе фигурных скобок применяется к обработке второго файла... по мере чтения каждой строки, он сравнивается со всеми элементами a[], и если они найдены, строка печатается. Это все люди!

Ответ 4

Просто для удовольствия, здесь версия Perl:

#!/usr/bin/perl
use strict;
use warnings;
my %patterns;
my $srch;

# Open file and get patterns to search for
open(my $fh2,"<","file2")|| die "ERROR: Could not open file2";
while (<$fh2>)
{
   chop;
   $patterns{$_}=1;
}

# Now read data file
open(my $fh1,"<","file1")|| die "ERROR: Could not open file1";
while (<$fh1>)
{
   (undef,$srch,undef)=split;
   print $_ if defined $patterns{$srch};
}

Вот несколько таймингов, используя 60 000 строк file1 и 6000 строк файла2 для каждого метода создания файла Ed:

time awk 'NR==FNR{pats[$0]; next} $2 in pats' file2 file1 > out
real    0m0.202s
user    0m0.197s
sys     0m0.005s

time ./go.pl > out2
real    0m0.083s
user    0m0.079s
sys     0m0.004s

Ответ 5

Просто для изучения: я решал ту же проблему, и я придумал различные решения (включая read $line петли и т.д.). Когда я добрался до найденного выше grep с одним слоем, я все равно получил неправильный вывод. Затем я понял, что у моего файла PATTERN есть две завершающие строки... Итак, grep взял все мои строки из моей базы данных. Мораль: проверить конечные пробелы/строки. Кроме того, вы запустили команду на гораздо большем наборе данных с несколькими сотнями шаблонов и time не могли даже подсчитать.