ファイルから行を削除するより高速な方法はありますか?

ファイルから行を削除するより高速な方法はありますか?

関連する質問はここ

大容量ファイルを編集してみると、途中で数行を削除する必要があることがよくあります。削除する行を知っており、通常は次のことを行います。

sed "linenum1,linenum2 d" input.txt > input.temp

または、-iオプションを追加してインラインで実行することもできます。行番号を知っていますが、ストリームの編集を避け、特定の行だけを削除する命令がありますか? input.txtは最大50GBまで可能です。

ベストアンサー1

ファイルのコピーを書き込まない場合は、次のようにファイル自体にファイルを書き込むことをお勧めします。

{
  sed "$l1,$l2 d" < file
  perl -le 'truncate STDOUT, tell STDOUT'
} 1<> file

バックアップコピーがないため危険です。

または、これを防ぐために、sedmanatworkのアイデアの一部を盗んでください。

{
  head -n "$(($l1 - 1))"
  head -n "$(($l2 - $l1 + 1))" > /dev/null
  cat
  perl -le 'truncate STDOUT, tell STDOUT'
} < file 1<> file

最初の項目が上書きされるため、この問題はまだ改善される可能性があります。l1-1これを行う必要はありませんが、これを避けることは、perl最終的に効率が低い可能性があるすべてのタスクを実行するなど、プログラミングにさらに参加することを意味します。

perl -ne 'BEGIN{($l1,$l2) = ($ENV{"l1"}, $ENV{"l2"})}
    if ($. == $l1) {$s = tell(STDIN) - length; next}
    if ($. == $l2) {seek STDOUT, $s, 0; $/ = \32768; next}
    if ($. > $l2) {print}
    END {truncate STDOUT, tell STDOUT}' < file 1<> file

次の出力から1000000〜1000050行のいくつかのタイミングを削除しますseq 1e7

  • sed -i "$l1,$l2 d" file:16.2秒
  • 最初のソリューション:1.25秒
  • 2番目のソリューション:0.057秒
  • 3番目のソリューション:0.48秒

< fileすべて同じ原則に従います。ファイルの2つのファイル記述子を開きます。 1つは読み取り専用モード(0)で略語forを使用し、もう1つは0< file読み取り/書き込みモード(1)で(will be)を使用します。このファイル記述子は2つを指します。1<> file<> file0<> fileファイル説明を開くそれぞれ電流があります。カーソル位置それらに関連するファイルから。

たとえば、2番目のソリューションでは、最初のソリューションはfd 0から行データをhead -n "$(($l1 - 1))"読み取り、そのデータをfd 1に書き込みます。$l1 - 1したがって、コマンドが終了すると、カーソルは2つのコマンドの間にあります。ファイル説明を開くfds 0と1に関連する項目は、3行目の先頭にあります$l1

次に、head -n "$(($l2 - $l1 + 1))" > /dev/null同じhead行を読みます。$l2 - $l1 + 1ファイル説明を開くまだ接続されているfd 0を介して、fd 0のカーソルはその行の次の行の先頭に移動します$l2

ただし、fd 1はにリダイレクトされているため、/dev/nullfd 1を作成した後はカーソルを次に移動しません。ファイル説明を開く{...}fd 1が指します。

したがって、起動時にcatカーソルは次の位置にあります。ファイル説明を開くfd 0が指す位置は次の行の先頭にあり$l2、fd 1のカーソルはまだ$l1行3の先頭にあります。つまり、対応する2head行目は入力では削除のためにスキップされますが、出力では削除されません。これで、最初の行は次の行で上書きされ、catこのように続行されます。$l1$l2

catfd 0のファイルの終わりに達すると返されます。ただし、fd 1はまだ上書きされていないファイルの場所を指します。このセクションは消える必要があり、ファイルの最後に移動された削除された行が占めるスペースに対応します。私たちに必要なのは、現在fd 1が指す正確な場所からファイルを切り取ることです。

これはftruncateシステムコールを介して行われます。残念ながら、これを実行できる標準のUnixユーティリティがないため、fd 1に関連する現在のカーソル位置をperl提供することに依存しています。私たちはPerlのシステムコールインターフェイスをtell STDOUT使ってこのオフセットからファイルを切り取りますftruncatetruncate

head3番目のソリューションでは、最初のコマンドのfd 1書き込みをシステムコールに置き換えますlseek

おすすめ記事