我必须从包含特定字符串的文件(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 #&
答案 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。对于每个内循环,你开始cat
和awk
(你不需要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