更快的grep函数适用于大(27GB)文件

时间:2013-01-30 11:31:20

标签: file bash awk grep

我必须从包含特定字符串的文件(5MB)中获取大文件(27GB)中相同的字符串(和其他信息)。 为了加快分析速度,我将27GB文件拆分为1GB文件,然后应用以下脚本(在这里有些人的帮助下)。但是效率不高(生成180KB文件需要30个小时!)。

这是脚本。有比grep更合适的工具吗?还是一种更有效的方式来使用grep?

#!/bin/bash

NR_CPUS=4
count=0


for z in `echo {a..z}` ;
do
 for x in `echo {a..z}` ;
 do
  for y in `echo {a..z}` ;
  do
   for ids in $(cat input.sam|awk '{print $1}');  
   do 
    grep $ids sample_"$z""$x""$y"|awk '{print $1" "$10" "$11}' >> output.txt &
    let count+=1
                                [[ $((count%NR_CPUS)) -eq 0 ]] && wait
   done
  done #&

4 个答案:

答案 0 :(得分:14)

您可以尝试一些事项:

1)您正在多次阅读input.sam。它只需要在第一次循环开始之前读取一次。将ids保存到临时文件中,该文件将由grep读取。

2)使用LC_ALL=C作为grep命令的前缀,以使用C语言环境而不是UTF-8。这将加快grep

3)使用fgrep,因为您正在搜索固定字符串,而不是正则表达式。

4)使用-f从文件中获取grep个读取模式,而不是使用循环。

5)不要从多个进程写入输出文件,因为最终可能会出现行交错和损坏的文件。

进行这些更改后,这就是您的脚本将成为的内容:

awk '{print $1}' input.sam > idsFile.txt
for z in {a..z}
do
 for x in {a..z}
 do
  for y in {a..z}
  do
    LC_ALL=C fgrep -f idsFile.txt sample_"$z""$x""$y" | awk '{print $1,$10,$11}'
  done >> output.txt

另外,请查看旨在帮助您并行运行作业的GNU Parallel

答案 1 :(得分:4)

我最初的想法是你反复产生grep。产生过程非常昂贵(相对),我认为你最好使用某种脚本化解决方案(例如Perl),不需要不断创建过程

e.g。对于每个内循环,你开始catawk(你不需要cat,因为awk可以读取文件,事实上这不是cat awk 1}} / grep组合每次返回相同的内容?)然后greps。然后你等待4 grep完成,然后你又回去了。

如果必须使用grep -f filename ,则可以使用

{{1}}

指定要在文件名中匹配的模式集,而不是命令行上的单个模式。我怀疑从上面你可以预先生成这样一个列表。

答案 2 :(得分:0)

好的我有一个包含4个字符串的测试文件,即aaaa aaab aaac等

ls -lh test.txt
-rw-r--r-- 1 root pete 1.9G Jan 30 11:55 test.txt
time grep -e aaa -e bbb test.txt
<output>
real    0m19.250s
user    0m8.578s
sys     0m1.254s


time grep --mmap -e aaa -e bbb test.txt
<output>
real    0m18.087s
user    0m8.709s
sys     0m1.198s

因此,使用mmap选项显示对具有两种搜索模式的2 GB文件的明显改进,如果您采用@BrianAgnew的建议并使用grep的单个调用,请尝试使用--mmap选项。

虽然应该注意,如果源文件在搜索期间发生变化,mmap可能会有点古怪。 来自man grep

  

- MMAP

     

如果可能,使用mmap(2)系统调用来读取输入,而不是默认的read(2)系统调用。在某些情况下, - mmap会产生更好的性能。但是,如果输入文件在grep运行时收缩,或者发生I / O错误,则--mmap可能导致未定义的行为(包括核心转储)。

答案 3 :(得分:0)

使用GNU Parallel,它看起来像这样:

awk '{print $1}' input.sam > idsFile.txt
doit() {
   LC_ALL=C fgrep -f idsFile.txt sample_"$1" | awk '{print $1,$10,$11}'
}
export -f doit
parallel doit {1}{2}{3} ::: {a..z} ::: {a..z} ::: {a..z} > output.txt

如果行的顺序不重要,那会更快一些:

parallel --line-buffer doit {1}{2}{3} ::: {a..z} ::: {a..z} ::: {a..z} > output.txt