かなり大きなファイルを1行ずつ読み、各行でいくつかの処理を実行し、結果を別のファイルに書き込むbashスクリプトがあります。現在私が使用しているecho
結果ファイルの末尾に追加されますが、ファイルのサイズが大きくなるほど速度が遅くなります。だから私の質問は、大容量ファイルに行を追加する簡単な方法は何ですか?
ファイルに行が追加される順序は私とは関係がないので、次に追加します。スタートまたは終わりまたは何でもランダムファイルの場所。 RAMが多くのサーバーでスクリプトを実行しているため、結果を変数に保存して最後に内容全体を作成すると、より高速になります。これも私にとってはうまくいきます。
実際には2つのスクリプトがあり、ここにそれぞれの例を入れました。 (実際のスクリプトの一部ですが、単純化のためにいくつかの部分を削除しました。
while read line
do
projectName=`echo $line | cut -d' ' -f1`
filepath=`echo $line | cut -d' ' -f2`
numbers=`echo $line | cut -d' ' -f3`
linestart=`echo $numbers | cut -d: -f2`
length=`echo $numbers | cut -d: -f3`
lang=`echo $line | cut -d' ' -f9`
cloneID=`echo $line | cut -d' ' -f10`
cloneSubID=`echo $line | cut -d' ' -f11`
minToken=`echo $line | cut -d' ' -f12`
stride=`echo $line | cut -d' ' -f13`
similarity=`echo $line | cut -d' ' -f14`
currentLine=$linestart
endLine=$((linestart + length))
while [ $currentLine -lt $endLine ];
do
echo "$projectName, $filepath, $lang, $linestart, $currentLine, $cloneID, $cloneSubID, $minToken, $stride, $similarity"
currentLine=$((currentLine + 1))
done
done < $filename
上記のコードを使用する方法は次のとおりです。./script filename > outputfile
2番目のスクリプトは次のとおりです。
while read -r line;
do
echo "$line" | grep -q FILE
if [ $? = 0 ];
then
if [[ $line = *"$pattern"* ]];
then
line2=`echo "${line//$pattern1/$sub1}" | sed "s#^[^$sub1]*##"`
newFilePath=`echo "${line2//$pattern2/$sub2}"`
projectName=`echo $newFilePath | sed 's#/.*##'`
localProjectPath=`echo $newFilePath | sed 's#^[^/]*##' | sed 's#/##'`
cloneID=$cloneCounter
revisedFile="revised-$postClusterFile-$projectName"
overallRevisedFile="$cluster_dir/revised-overall-post-cluster"
echo $projectName $localProjectPath $lang $cloneID $cloneSubID $minToken $stride $similarity >> $overallRevisedFile
cloneSubID=$((cloneSubID + 1))
fi
fi
done < $cluster_dir/$postClusterFile
2番目のコードの使用法は次のとおりです。./script input output
修正する
まあ、明らかに犯人はバックティックの広範な使用です。最初のスクリプトは大幅に変更され、過去50分に比べて2分で実行されます。私はそれに非常に満足しています。次のコードを提供した@BinaryZebraに感謝します。
while read -r projectName filepath numbers a a a a a lang cloneID cloneSubID minToken stride similarity;
do
IFS=':' read -r a linestart length <<<"$numbers"
currentLine=$linestart
endLine=$((linestart + length))
while [ $currentLine -lt $endLine ]; do
echo "$projectName, $filepath, $lang, $linestart, $currentLine, $cloneID, $cloneSubID, $minToken, $stride, $similarity"
currentLine=$((currentLine + 1))
done
done < $filename >>$outputfile
しかし、2番目のスクリプトでは、次のように修正しました(ここで実際のスクリプトもさらに追加しました)。
while read -r line;
do
echo "$line" | grep -q FILE
if [ $? = 0 ];
then
if [[ $line = *"$pattern"* ]];
then
IFS=$'\t' read -r a a filetest <<< "$line"
filetest="${filetest#*$pattern1}"
projectName="${filetest%%/*}"
localProjectPath="${filetest#*/}"
cloneID=$cloneCounter
revisedFile="revised-$postClusterFile-$projectName"
echo $projectName $localProjectPath $lang $cloneID $cloneSubID $minToken $stride $similarity
cloneSubID=$((cloneSubID + 1))
fi
else
echo "This is a line: $line" | grep -q \n
if [ $? = 0 ];
then
cloneCounter=$((cloneCounter + 1))
cloneSubID=0
fi
fi
done < $cluster_dir/$postClusterFile >> $overallRevisedFile
以前よりはるかに速くなりました(7分対20分)。しかし、より速くする必要があり、より大きなテストでは、まだ速度が遅くなることを感じます。約24時間実行され、現在の出力サイズは200MBに近いです。出力ファイルが3GB程度になると予想しているので、2週間かかることもありますが、そんな余裕はありません。出力のサイズ/増加も非線形であるため、時間の経過とともに速度が遅くなります。
私ができる他のことがありますか、それともそれですか?
ベストアンサー1
いくつかのアイデア:
1. - 各行で繰り返しカットを呼び出すのではなく、読み取りを活用します。
切り取る変数のリストは次の' '
とおりです。
projectName 1
filepath 2
numbers 3
lang 9
cloneID 10
cloneSubID 11
minToken 12
stride 13
similarity 14
これは次のことを読んで直接行うことができます。
while read -r projectName filepath numbers a a a a a lang cloneID cloneSubID minToken stride similarity;
生産ラインは長いが処理時間は短い。変数aは、未使用の値のスペースを埋めるために存在します。
2.- ':'で割る変数番号を再処理するには、次のようにすることができます(あなたの質問にはbashタグが付けられています)。
IFS=':' read -r a linestart length <<<"$numbers"
これにより、コードは次のように単純化されます。
while read -r projectName filepath numbers a a a a a lang cloneID cloneSubID minToken stride similarity;
do
IFS=':' read -r a linestart length <<<"$numbers"
currentLine=$linestart
endLine=$((linestart + length))
while [ $currentLine -lt $endLine ]; do
echo "$projectName, $filepath, $lang, $linestart, $currentLine, $cloneID, $cloneSubID, $minToken, $stride, $similarity"
currentLine=$((currentLine + 1))
done
done < $filename >>$outputfile
3.- 2番目のスクリプトでは、sub1および/またはsub2変数が何であるかについての説明はありません。
4. - 一般的に言えば、スクリプトを一連の小さなスクリプトに分割できる場合は、各スクリプトの時間を測定して時間がかかる領域を見つけることができます。
5. - そして他の答えが示すように、ファイル(およびすべての中間結果)をメモリパーティションに配置すると、最初のファイルをより速く読み取ることができます。後でスクリプトを実行すると、メモリ内キャッシュから読み取られ、改善が隠されます。このガイド役に立ちます。