bashスクリプトを使用して、巨大なテキストファイルから最初の行を繰り返し削除する必要があります。
現在使用sed -i -e "1d" $FILE
していますが、削除には1分ほどかかります。
これを達成するためのより効率的な方法はありますか?
しっぽを試してみてください:
tail -n +2 "$FILE"
-n x
:最後のx
行を印刷するだけです。tail -n 5
入力の最後の5行が表示されます。+
記号の種類は引数を反転し、tail
最初の行以外のものを印刷しますx-1
。ファイル全体、最初の行以外のすべてなどtail -n +1
を印刷します。tail -n +2
GNUはよりtail
もはるかに高速ですsed
。tail
BSDでも利用可能であり、-n +2
フラグは両方のツールで一貫しています。詳細については、FreeBSDまたはOSXのマニュアルページを確認してください。
ただし、 BSDバージョンはよりもはるかに遅くなる可能性がsed
あります。彼らはどうやってそれを管理したのだろうか。スクリプトの解釈や正規表現の適用など、非常に複雑な操作を行いtail
ながら、ファイルを1行ずつ読み取る必要があります。sed
注:使用したくなるかもしれません
# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"
しかし、これはあなたに空のファイルを与えるでしょう。その理由は、リダイレクト(>
)がtail
シェルによって呼び出される前に発生するためです。
$FILE
tail
tail
シェルはプロセスのstdoutをにリダイレクトします$FILE
tail
今空から読む$FILE
ファイル内の最初の行を削除する場合は、次を使用する必要があります。
tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"
問題が発生した&&
ときにファイルが上書きされないようにします。
「>」演算子を使用せずに -i を使用してファイルを更新できます。次のコマンドは、ファイルから最初の行を削除し、ファイルに保存します (バックグラウンドで一時ファイルを使用します)。
sed -i '1d' filename
非GNUであるSunOSを使用している場合は、次のコードが役立ちます。
sed '1d' test.dat > tmp.dat
これは次の方法で簡単に実行できます。
cat filename | sed 1d > filename_without_first_line
コマンドラインで; -i
または、ファイルの最初の行を完全に削除するには、フラグを指定して sed のインプレース モードを使用します。
sed -i 1d <filename>
いいえ、それはあなたが得ようとしているのと同じくらい効率的です. 少し速くジョブを実行できるCプログラムを作成できますが(起動時間と処理引数が少なくなります)、ファイルが大きくなると、おそらくsedと同じ速度になる傾向があります(1分かかる場合はファイルが大きいと思います) )。
しかし、あなたの質問は、解決策を前提としているという点で、他の多くの質問と同じ問題に苦しんでいます。どうやってやろうとしているかではなく、何をしようとしているのかを詳しく教えていただければ、より良い選択肢を提案できるかもしれません。
たとえば、これが他のプログラム B が処理するファイル A である場合、1 つの解決策は、最初の行を削除するのではなく、プログラム B を変更して別の方法で処理することです。
すべてのプログラムがこのファイル A に追加され、プログラム B が現在、削除する前に最初の行を読み取って処理しているとします。
プログラム B を再設計して、最初の行を削除しようとせず、ファイル A への永続的な (おそらくファイルベースの) オフセットを維持して、次に実行するときにそのオフセットをシークして、プロセスを処理できるようにすることができます。そこに行を追加し、オフセットを更新します。
次に、静かな時間 (真夜中?) に、ファイル A の特別な処理を実行して、現在処理されているすべての行を削除し、オフセットを 0 に戻すことができます。
プログラムがファイルを開いて再書き込みするよりも、ファイルを開いてシークする方が確実に高速になります。もちろん、この議論はプログラム B を制御できることを前提としています。それが事実かどうかはわかりませんが、さらに情報を提供していただければ、他の解決策があるかもしれません。
パックスが言ったように、おそらくこれ以上速くなることはないでしょう。その理由は、ファイルの先頭からの切り捨てをサポートするファイルシステムがほとんどないため、ファイルのサイズがO( n
) 操作になるためです。ただし、はるかにn
高速にできることは、最初の行を同じバイト数 (おそらくスペースまたはコメント) で上書きすることです。
その場でファイルを編集できます: 次のようにperl の-i
フラグを使用するだけです:
perl -ni -e 'print unless $. == 1' filename.txt
あなたが尋ねるように、これは最初の行を非表示にします。Perl はファイル全体を読み取ってコピーする必要がありますが、出力が元のファイルの名前で保存されるように調整します。
最初の行を除く行を表示する必要があります。
cat textfile.txt | tail -n +2
csplitを使ってみませんか?
man csplit
csplit -k file 1 '{1}'
このワンライナーでできます:
echo "$(tail -n +2 "$FILE")" > "$FILE"
tail
ファイルのロックが解除される前に実行されるecho
ため、一時ファイルは必要ありません。
削除を高速化できないように聞こえるので、次のようにファイルをバッチで処理するのが良い方法だと思います。
While file1 not empty
file2 = head -n1000 file1
process file2
sed -i -e "1000d" file1
end
これの欠点は、プログラムが途中で強制終了された場合 (または、そこに悪い SQL がある場合 - 「プロセス」部分が停止またはロックアップする原因となる場合)、スキップされるか、2 回処理される行があることです。 .
(file1 には sql コードの行が含まれています)
あなたがしようとしているのが障害後の回復である場合は、これまでに行ったことを含むファイルを作成するだけで済みます。
if [[ -f $tmpf ]] ; then
rm -f $tmpf
fi
cat $srcf |
while read line ; do
# process line
echo "$line" >> $tmpf
done
N-1 行で tail を使用してそれをファイルに送信し、その後古いファイルを削除して、新しいファイルの名前を古い名前に変更すると、うまくいきますか?
これをプログラムで行っていた場合、各行を読み取った後、ファイル全体を読み取り、ファイルのオフセットを覚えているので、その位置に戻って、1 行少ないファイルを読み取ることができます。