7

我想在带有 -f 的文本文件上使用 grep 来匹配一长串(10,000)模式。原来 grep 不喜欢这个(谁知道?)。一天后,它没有产生任何东西。较小的列表几乎可以立即生效。

我在想我可以把我的长长的名单分开做几次。知道模式列表的最大长度可能是多少吗?

另外,我对unix很陌生。欢迎替代方法。模式列表或搜索项位于纯文本文件中,每行一个。

谢谢大家的指导。

4

5 回答 5

8

从评论来看,您匹配的模式似乎是固定字符串。如果是这种情况,您绝对应该使用-F. 这将大大提高匹配的速度。(在中等功率的机器上使用 479,000 个字符串匹配 3 行的输入文件 using-F需要不到 1.5 秒。不使用-F,同一台机器在几分钟后还没有完成。)

于 2013-01-21T14:15:32.713 回答
6

我遇到了同样的问题。在一个包含 900 万行的文件中搜索 400 万个模式。好像是RAM的问题。所以我得到了这个整洁的小工作,它可能比拆分和加入要慢,但它只需要这一行。

 while read line; do grep $line fileToSearchIn;done < patternFile

我需要使用该解决方法,因为该-F标志不是该大文件的解决方案...

编辑:这对于大文件来说似乎真的很慢。经过更多研究后,我发现了来自 Kent NGS-editing-Tools的“faSomeRecords”和其他很棒的工具

我自己尝试从 550 万条记录文件中提取 200 万条 fasta-rec。花了大约。30秒..

干杯

编辑:直接下载链接

于 2013-10-24T12:45:51.467 回答
1

这是一个 bash 脚本,您可以在文件上运行(或者,如果您愿意,也可以是文件的子集)。它将密钥文件分割成越来越大的块,并为每个块尝试 grep 操作。这些操作是计时的——现在我正在计时每个 grep 操作,以及处理所有子表达式的总时间。输出以秒为单位 - 通过一些努力,您可以获得毫秒,但您遇到的问题不太可能需要这种粒度。使用以下形式的命令在终端窗口中运行脚本

./timeScript keyFile textFile 100 > outputFile

这将运行脚本,使用 keyFile 作为存储搜索键的文件,使用 textFile 作为您要查找键的文件,并使用 100 作为初始块大小。在每个循环中,块大小将加倍。

在第二个终端中,运行命令

tail -f outputFile

这将跟踪您的其他进程的输出到文件中outputFile

我建议您打开第三个终端窗口,并top在该窗口中运行。您将能够看到您的进程占用了多少内存和 CPU - 同样,如果您看到大量内存消耗,它会提示您事情进展不顺利。

这应该可以让您找出事情何时开始放缓 - 这就是您问题的答案。我不认为有一个“神奇的数字”——它可能取决于你的机器,特别是文件大小和你拥有的内存量。

您可以获取脚本的输出并将其通过 grep:

grep entire outputFile

您最终会得到摘要 - 块大小和所花费的时间,例如

Time for processing entire file with blocksize 800: 4 seconds

如果您将这些数字相互绘制(或简单地检查数字),您将看到算法何时最佳,何时变慢。

这是代码:我没有进行广泛的错误检查,但它似乎对我有用。显然,在你的最终解决方案中,你需要对 grep 的输出做一些事情(而不是wc -l像我做的那样通过管道来查看匹配了多少行)......

#!/bin/bash
# script to look at difference in timing
# when grepping a file with a large number of expressions
# assume first argument = name of file with list of expressions
# second argument = name of file to check
# optional third argument = initial block size (default 100)
#
# split f1 into chunks of 1, 2, 4, 8... expressions at a time
# and print out how long it took to process all the lines in f2

if (($# < 2 )); then
  echo Warning: need at leasttwo parameters.
  echo Usage: timeScript keyFile searchFile [initial blocksize]
  exit 0
fi

f1_linecount=`cat $1 | wc -l`
echo linecount of file1 is $f1_linecount

f2_linecount=`cat $2 | wc -l`
echo linecount of file2 is $f2_linecount
echo

if (($# < 3 )); then
  blockLength=100
else
  blockLength=$3
fi

while (($blockLength < f1_linecount))
do
  echo Using blocks of $blockLength
  #split is a built in command that splits the file
  # -l tells it to break after $blockLength lines
  # and the block$blockLength parameter is a prefix for the file
  split -l $blockLength $1 block$blockLength
  Tstart="$(date +%s)"
  Tbefore=$Tstart

  for fn in block*
    do
      echo "grep -f $fn $2 | wc -l"
      echo number of lines matched: `grep -f $fn $2 | wc -l`
      Tnow="$(($(date +%s)))"
      echo Time taken: $(($Tnow - $Tbefore)) s
      Tbefore=$Tnow
    done
  echo Time for processing entire file with blocksize $blockLength: $(($Tnow - $Tstart)) seconds
  blockLength=$((2*$blockLength))
  # remove the split files - no longer needed
  rm block*
  echo block length is now $blockLength and f1 linecount is $f1_linecount
done

exit 0
于 2013-01-20T23:32:46.297 回答
0

你当然可以尝试 sed 看看你是否得到了更好的结果,但是在任何大小的文件上都需要做很多工作。您没有提供有关您的问题的任何详细信息,但是如果您有 10k 模式,我会尝试考虑是否有某种方法可以将它们概括为较少数量的正则表达式。

于 2013-01-20T23:51:33.930 回答
0

这是一个 perl 脚本“match_many.pl”,它解决了“大量键与大量记录”问题的一个非常常见的子集。从标准输入每行接受一个键。两个命令行参数是要搜索的文件的名称和必须匹配键的字段(空格分隔)。原始问题的这个子集可以快速解决,因为记录中匹配(如果有)的位置是提前知道的,并且键始终对应于记录中的整个字段。在一个典型的案例中,它使用 42899 个键搜索了 9400265 条记录,匹配​​了 42401 个键并在 41 秒内发出了 1831944 条记录。更一般的情况,其中键可能作为子字符串出现在记录的任何部分,是这个脚本没有解决的更困难的问题。

#!/usr/bin/perl -w
use strict;
use warnings;
my $kcount;
my ($infile,$test_field) = @ARGV;
if(!defined($infile) || "$infile" eq "" || !defined($test_field) || ($test_field <= 0)){
  die "syntax: match_many.pl infile field" 
}
my %keys;       # hash of keys
$test_field--;  # external range (1,N) to internal range (0,N-1)

$kcount=0;
while(<STDIN>) {
   my $line = $_;
   chomp($line);
   $keys {$line} = 1;
   $kcount++
}
print STDERR "keys read: $kcount\n";

my $records = 0;
my $emitted = 0;
open(INFILE, $infile )  or die "Could not open $infile";
while(<INFILE>) {
   if(substr($_,0,1) =~ /#/){ #skip comment lines
     next;
   }
   my $line = $_;
   chomp($line);
   $line =~ s/^\s+//;
   my @fields = split(/\s+/, $line);
   if(exists($keys{$fields[$test_field]})){
      print STDOUT "$line\n";
      $emitted++;
      $keys{$fields[$test_field]}++;
   }
   $records++;
}

$kcount=0;
while( my( $key, $value ) = each %keys ){
   if($value > 1){ 
      $kcount++; 
   }
}

close(INFILE);
print STDERR "records read: $records, emitted: $emitted; keys matched: $kcount\n";

exit;
于 2016-11-04T18:51:46.903 回答