2016-02-19 120 views
0

我有一个4 GB的文件,我需要做一些操作。我有一个Bash脚本来做到这一点,但它Bash似乎不适合将大型数据文件读入数组。所以我决定用awk分解我的文件。高效地分割大文件(目前使用awk)

我现在的脚本是:

for((i=0; i<100; i++)); do awk -v i=$i 'BEGIN{binsize=60000}{if(binsize*i < NR && NR <= binsize*(i+1)){print}}END{}' my_large_file.txt &> my_large_file_split$i.fastq; done 

但是这个脚本的问题是,它会和循环通读这个大文件100倍(这大概会导致对IO的400GB)。

问题:有没有更好的策略在大文件中阅读一次?也许在awk中写文件而不是重定向它的输出?

+1

为什么不使用'split(1)'? – peak

+0

这似乎也是一个合理的解决方案。 –

回答

1

假设binsize是每个块需要的行数,您可以在逐步执行文件时维护和重置行计数器,并在awk中设置备用输出文件,而不是使用shell重定向。

awk -v binsize=60000 ' 
    BEGIN { 
    outfile="output_chunk_1.txt" 
    } 
    count > binsize { 
    if (filenum>1) { 
     close(outfile) 
    } 
    filenum++ 
    outfile="output_chunk_" filenum ".txt" 
    count=0 
    } 
    { 
    count++ 
    print > outfile 
    } 
' my_large_file.txt 

我没有实际测试此代码,因此,如果不逐字工作,至少也应该给你使用的策略的想法。 :-)

这个想法是,我们将逐步通过文件,每当我们的块的行数超过binsize时,更新变量中的文件名。请注意,close(outfile)并不是绝对必要的,因为awk当然会在退出时关闭任何打开的文件,但它可以为每个打开的文件句柄节省几个字节的内存(这只有在许多输出文件)。


这就是说,你可以做几乎同样的事情在bash独自:

#!/usr/bin/env bash 

binsize=60000 

filenum=1; count=0 

while read -r line; do 

    if [ $count -gt $binsize ]; then 
    ((filenum++)) 
    count=0 
    fi 

    ((count++)) 

    outfile="output_chunk_${filenum}.txt" 
    printf '%s\n' "$line" >> $outfile 

done < my_large_file.txt 

(也未经测试。)

虽然我倒是希望 awk的解决方案比bash快,它可能不会伤害你自己的基准。 :)

+0

我没有机会测试你的特定脚本,但输出awk分离文件的关键是'outfile =“output_chunk_”filenum“.txt”'和'print> outfile'(我用这个问题来解决这个问题)。与我原先的剧本(我预计需要800分钟)相比,此方法大约需要30分钟才能完成。谢谢! –