sedを使用して大きなファイルの効率的なインプレースヘッダーを削除しますか?


24

以下のコマンドは、ファイルサイズによって数分かかる場合があります。より効率的な方法はありますか?

sed -i 1d large_file 

回答:


34

ed代わりに試してください:

ed <<< $'1d\nwq' large_file

その「大きい」とは、約1,000万行以上を意味する場合、より適切に使用しますtail。インプレース編集はできませんが、そのパフォーマンスにより、その不足が許されます。

tail -n +2 large_file > large_file.new

編集して、時差を表示します。

awk同じマシン(CPU 2.2GHz)での実行時間を持つためにJaypalによるコードが追加されました。)

bash-4.2$ seq 1000000 > bigfile.txt # further file creations skipped

bash-4.2$ time sed -i 1d bigfile.txt
time 0m4.318s

bash-4.2$ time ed -s <<< $'1d\nwq' bigfile.txt
time 0m0.533s

bash-4.2$ time perl -pi -e 'undef$_ if$.==1' bigfile.txt
time 0m0.626s

bash-4.2$ time { tail -n +2 bigfile.txt > bigfile.new && mv -f bigfile.new bigfile.txt; }
time 0m0.034s

bash-4.2$ time { awk 'NR>1 {print}' bigfile.txt > newfile.txt && mv -f newfile.txt bigfile.txt; }
time 0m0.328s

以下の場合はtail、私はむしろ実行する時間カウントします両方の最初の行を削除し、交換するbigfile.txtとしbigfile.new
rozcietrzewiacz

@rozcietrzewiacz、あなたのポイントは正しいです。ありがとうございました。更新しました。
マナトワーク

これは本当にクールです!私は同じことをやったawkし、次の結果を得た-[jaypal:~/Temp] seq 1000000 > bigfile.txt [jaypal:~/Temp] time awk 'NR>1 {print}' bigfile.txt >newfile.txt real 0m0.649s user 0m0.601s sys 0m0.033s
jaypalシン

1
@Jaypal、私はあなたのコードを選択肢のリストに追加しました。私のマシンではさらに高速でした。奇妙なことに、私はawkのパフォーマンスがに近づくと予想していましたsed。(自分への注意:期待しないでください-代わりにテストしてください。)
マナトワーク

私の場合、これが最良の解決策でしたtail -n +2 bigfile.txt > bigfile.new && mv -f bigfile.new bigfile.txt;。複数のプロセスで使用される単一のタスクリストを追跡するために、ロック付きの単一のファイルを使用しています。最初のポスターで使用したものから始めましたsed -i 1d large_file 。そのため、ファイルが1〜2秒間ロックされていました。tail/mvコンボはほぼ瞬時に完了します。ありがとうございました!
クリスアダムス

6

ファイルの先頭から物事を効率的に削除する方法はありません。最初からデータを削除するには、ファイル全体を書き直す必要があります。

ただし、ファイルの末尾から切り捨てるのは非常に高速です(OSはファイルサイズ情報を調整するだけで、現在使用されていないブロックをクリアすることができます)。通常、ファイルの先頭から削除しようとすると、これは不可能です。

ブロック/エクステント全体を正確に削除した場合、理論的には「高速」になりますが、そのためのシステムコールがないため、ファイルシステム固有のセマンティクス(存在する場合)に依存する必要があります。(または、ファイルの実際の開始を示すために、最初のブロック/エクステント内に何らかの形のオフセットを持っていると思います。それも聞いたことがないでしょう。)


ファイルが非常に大きい場合、I / Oオーバーヘッドは、行末の処理に必要なCPUオーバーヘッドよりも(おそらくはるかに)大きくなる可能性があります。
マット

あなたが正しいです。ただし、ツールがファイルコンテンツにアクセスする方法に違いがある場合があります。最良の方法は、不要な場合は1行ずつ処理しないか、少なくとも不要な場合は1行ずつ読み取らないことです。
マナトワーク

2
結果の違いが非常に大きく、ここでそのファイルサイズで再現できることに驚いています。ただし、ファイルサイズが大きくなると利点は減少するようです(seq 10M、sedで15秒、edで5秒で試してみました)。とにかく良いヒント(+1)。
マット

バージョン3.15以降、Linuxにはある程度のエクステントベースのファイルシステムでファイルの一部を折りたたむ APIがありますが、少なくともext4の場合は完全なブロック(通常4k)でのみ実行できます。
ステファンシャゼル14年

編集でファイル全体を書き直す必要がある場合でも、コマンドラインツールを使用して効率的に編集すると便利な場合があります。私の場合、これはシステムRAMの合計よりも大きいファイルの最初の行を削除する必要があるときに役立ちました。
ジェイソン

3

最も効率的な方法、それをしないでください!どちらにしても、ディスク上に2倍の「大きな」スペースが必要になり、IOが無駄になります。

1行目なしで読みたい大きなファイルが残っている場合は、1行目を削除するために読み込む必要があるまで待ちます。ファイルをstdinからプログラムに送信する必要がある場合は、tailを使用して送信します。

tail -n +2 | your_program

ファイルを読み取る必要がある場合は、ディスクに必要なスペースがある場合に限り、1行目を削除することができます。

tail -n +2 | tee large_file2 | your_program

stdinから読み取れない場合は、fifoを使用します。

mkfifo large_file_wo_1st_line
tail -n +2 large_file > large_file_wo_1st_line&
your_program -i large_file_wo_1st_line

bashを使用している場合は、プロセス置換の利点を活用してください。

your_program -i <(tail -n +2 large_file)

ファイルを探す必要がある場合、そもそもファイルにこだわるよりも良い解決策はありません。このファイルがstdoutによって生成された場合:

large_file_generator | tail -n +2 > large_file

そうでない場合は、常にfifoまたはプロセス置換ソリューションがあります。

mkfifo large_file_with_1st_file
large_file_generator -o large_file_with_1st_file&
tail -n +2 large_file_with_1st_file > large_file_wo_1st_file

large_file_generator -o >(tail -n 2+ > large_file_wo_1st_file)


0

これは単なる理論ですが、...

カスタムファイルシステム(FUSEまたは同様のメカニズムを使用して実装)は、コンテンツが既存のディレクトリとまったく同じ場所にあるディレクトリを公開できますが、ファイルは必要に応じて切り捨てられます。ファイルシステムは、すべてのファイルオフセットを変換します。その後、時間のかかるファイルの書き換えを行う必要はありません。

しかし、このアイデアが非常に自明ではないことを考えると、数十テラバイトのそのようなファイルがない限り、そのようなファイルシステムを実装することは非常に高価であり、実用的ではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.