10

我必须从包含特定字符串的文件(5MB)中 grep 来自大文件(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

4 回答 4

16

您可以尝试几件事:

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

2) 为您的 grep 命令添加前缀LC_ALL=C以使用 C 语言环境而不是 UTF-8。这会加快速度grep

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

4)用于-fgrep文件中读取模式,而不是使用循环。

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 。

于 2013-01-30T12:13:22.510 回答
4

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

例如,对于您要开始的每个内部循环,cat并且awk(您不需要,cat因为awk可以读取文件,实际上这个cat/awk组合不是每次都返回相同的东西吗?)然后grep. 然后你等待 4greps完成,然后你再四处走动。

如果你必须使用grep,你可以使用

grep -f filename

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

于 2013-01-30T11:37:27.817 回答
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 可能会有点奇怪。从人 grep

--mmap

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

于 2013-01-30T12:08:29.800 回答
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
于 2019-03-05T18:15:57.100 回答