Eu tenho um script bash que lê um arquivo bastante grande, linha por linha, e para cada linha faz algum processamento e grava os resultados em outro arquivo. Atualmente, estou usando echo
para anexar ao final do arquivo de resultado, mas à medida que o tamanho do arquivo cresce, isso fica mais lento e lento. Então, minha pergunta é: qual é a maneira mais rápida de acrescentar linhas a um arquivo grande?
A ordem pela qual as linhas são adicionadas ao arquivo é irrelevante para mim, por isso estou aberto para adicionar ao início ou ao end ou a qualquer localização aleatória no arquivo. Eu também estou executando o script em um servidor com grandes quantidades de RAM, por isso, se segurar os resultados em uma variável e escrever a coisa toda no final é mais rápido, isso funciona para mim também.
Na verdade, existem 2 scripts, eu coloquei uma amostra de cada um aqui (eles são parte do script real, mas eu removi algumas partes por uma questão de simplicidade.
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
O código acima eu uso assim: ./script filename > outputfile
E o segundo script é assim:
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
O segundo código é usado como: ./script input output
Atualizar
OK, aparentemente, o principal culpado foi o uso extensivo de backticks.
O primeiro script foi strongmente modificado e agora é executado em 2 minutos em relação ao tempo de execução anterior de 50 minutos. Estou completamente feliz com isso. Obrigado a @BinaryZebra pelo seguinte Código:
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
Mas, para o segundo script, eu o modifiquei para parecer algo assim (também incluí um pouco mais do script atual aqui):
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
É muito mais rápido que antes: 7 minutos x 20 minutos, mas ainda preciso que seja mais rápido e ainda sinto a lentidão nos testes maiores. Ele está funcionando há aproximadamente 24 horas e o tamanho da saída é de quase 200 MB neste momento. Espero que o ficheiro de saída seja de aproximadamente 3 GB, o que poderá demorar 2 semanas, algo que não posso pagar. O tamanho / crescimento da saída também é não linear, diminuindo à medida que o tempo passa.
Existe mais alguma coisa que eu possa fazer ou é apenas o que é?