2016-02-19 63 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分鐘才能完成。謝謝! –