689

bashスクリプトを使用して、巨大なテキストファイルから最初の行を繰り返し削除する必要があります。

現在使用sed -i -e "1d" $FILEしていますが、削除には1分ほどかかります。

これを達成するためのより効率的な方法はありますか?

4

17 に答える 17

1243

しっぽを試してみてください:

tail -n +2 "$FILE"

-n x:最後のx行を印刷するだけです。tail -n 5入力の最後の5行が表示されます。+記号の種類は引数を反転し、tail最初の行以外のものを印刷しますx-1。ファイル全体、最初の行以外のすべてなどtail -n +1を印刷します。tail -n +2

GNUはよりtailもはるかに高速ですsedtailBSDでも利用可能であり、-n +2フラグは両方のツールで一貫しています。詳細については、FreeBSDまたはOSXのマニュアルページを確認してください。

ただし、 BSDバージョンはよりもはるかに遅くなる可能性がsedあります。彼らはどうやってそれを管理したのだろうか。スクリプトの解釈や正規表現の適用など、非常に複雑な操作を行いtailながら、ファイルを1行ずつ読み取る必要があります。sed

注:使用したくなるかもしれません

# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"

しかし、これはあなたに空のファイルを与えるでしょう。その理由は、リダイレクト(>)がtailシェルによって呼び出される前に発生するためです。

  1. シェルはファイルを切り捨てます$FILE
  2. シェルは、の新しいプロセスを作成しますtail
  3. tailシェルはプロセスのstdoutをにリダイレクトします$FILE
  4. tail今空から読む$FILE

ファイル内の最初の行を削除する場合は、次を使用する必要があります。

tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"

問題が発生した&&ときにファイルが上書きされないようにします。

于 2008-12-04T08:55:16.587 に答える
264

「>」演算子を使用せずに -i を使用してファイルを更新できます。次のコマンドは、ファイルから最初の行を削除し、ファイルに保存します (バックグラウンドで一時ファイルを使用します)。

sed -i '1d' filename
于 2014-11-24T07:10:07.467 に答える
80

非GNUであるSunOSを使用している場合は、次のコードが役立ちます。

sed '1d' test.dat > tmp.dat 
于 2013-02-19T07:32:38.597 に答える
18

これは次の方法で簡単に実行できます。

cat filename | sed 1d > filename_without_first_line

コマンドラインで; -iまたは、ファイルの最初の行を完全に削除するには、フラグを指定して sed のインプレース モードを使用します。

sed -i 1d <filename>
于 2018-11-22T14:34:38.633 に答える
16

いいえ、それはあなたが得ようとしているのと同じくらい効率的です. 少し速くジョブを実行できるCプログラムを作成できますが(起動時間と処理引数が少なくなります)、ファイルが大きくなると、おそらくsedと同じ速度になる傾向があります(1分かかる場合はファイルが大きいと思います) )。

しかし、あなたの質問は、解決策を前提としているという点で、他の多くの質問と同じ問題に苦しんでいます。どうやってやろうとしているかではなく、何をしようとしているのかを詳しく教えていただければより良い選択肢を提案できるかもしれません。

たとえば、これが他のプログラム B が処理するファイル A である場合、1 つの解決策は、最初の行を削除するのではなく、プログラム B を変更して別の方法で処理することです。

すべてのプログラムがこのファイル A に追加され、プログラム B が現在、削除する前に最初の行を読み取って処理しているとします。

プログラム B を再設計して、最初の行を削除しようとせず、ファイル A への永続的な (おそらくファイルベースの) オフセットを維持して、次に実行するときにそのオフセットをシークして、プロセスを処理できるようにすることができます。そこに行を追加し、オフセットを更新します。

次に、静かな時間 (真夜中?) に、ファイル A の特別な処理を実行して、現在処理されているすべての行を削除し、オフセットを 0 に戻すことができます。

プログラムがファイルを開いて再書き込みするよりも、ファイルを開いてシークする方が確実に高速になります。もちろん、この議論はプログラム B を制御できることを前提としています。それが事実かどうかはわかりませんが、さらに情報を提供していただければ、他の解決策があるかもしれません。

于 2008-12-04T03:19:12.163 に答える
10

パックスが言ったように、おそらくこれ以上速くなることはないでしょう。その理由は、ファイルの先頭からの切り捨てをサポートするファイルシステムがほとんどないため、ファイルのサイズがO( n) 操作になるためです。ただし、はるかにn高速にできることは、最初の行を同じバイト数 (おそらくスペースまたはコメント) で上書きすることです。

于 2008-12-04T03:48:49.823 に答える
10

その場でファイルを編集できます: 次のようにperl の-iフラグを使用するだけです:

perl -ni -e 'print unless $. == 1' filename.txt

あなたが尋ねるように、これは最初の行を非表示にします。Perl はファイル全体を読み取ってコピーする必要がありますが、出力が元のファイルの名前で保存されるように調整します。

于 2013-02-15T21:40:27.087 に答える
6

最初の行を除く行を表示する必要があります。

cat textfile.txt | tail -n +2
于 2016-09-29T07:42:01.580 に答える
4

csplitを使ってみませんか?

man csplit
csplit -k file 1 '{1}'
于 2009-03-04T16:08:02.983 に答える
3

このワンライナーでできます:

echo "$(tail -n +2 "$FILE")" > "$FILE"

tailファイルのロックが解除される前に実行されるechoため、一時ファイルは必要ありません。

于 2020-03-09T20:06:34.753 に答える
1

削除を高速化できないように聞こえるので、次のようにファイルをバッチで処理するのが良い方法だと思います。

While file1 not empty
  file2 = head -n1000 file1
  process file2
  sed -i -e "1000d" file1
end

これの欠点は、プログラムが途中で強制終了された場合 (または、そこに悪い SQL がある場合 - 「プロセス」部分が停止またはロックアップする原因となる場合)、スキップされるか、2 回処理される行があることです。 .

(file1 には sql コードの行が含まれています)

于 2008-12-04T03:40:33.633 に答える
0

あなたがしようとしているのが障害後の回復である場合は、これまでに行ったことを含むファイルを作成するだけで済みます。

if [[ -f $tmpf ]] ; then
    rm -f $tmpf
fi
cat $srcf |
    while read line ; do
        # process line
        echo "$line" >> $tmpf
    done
于 2009-11-14T01:42:44.770 に答える
-1

N-1 行で tail を使用してそれをファイルに送信し、その後古いファイルを削除して、新しいファイルの名前を古い名前に変更すると、うまくいきますか?

これをプログラムで行っていた場合、各行を読み取った後、ファイル全体を読み取り、ファイルのオフセットを覚えているので、その位置に戻って、1 行少ないファイルを読み取ることができます。

于 2008-12-04T03:50:44.440 に答える