ファイルの先頭(同じ場所、同じinode)で切り取ることはできますか?

ファイルの先頭(同じ場所、同じinode)で切り取ることはできますか?

file新しいファイルに書き込んで(> newfile)再移動しなくても()の末尾のバイトを削除できますmv newfile file。これは以下を介して行われますtruncate

truncate -s -1 file

前のバイトを削除できますが、それを移動すると(inodeが変更されます)(一部のtailバージョンの場合):

tail -c +1 file > newfile ; mv newfile file

それでは、ファイルを移動せずにこれを行う方法は?
理想的には切り取りと同様に、非常に大きなファイルの場合でも数バイトだけ変更するだけです。

注:sed -iファイルinodeを変更するので、役に立つとしてもこの質問に対する答えではありません(IMO)。

答え1

そしてksh93

tail -c+2 < file 1<>; file

(リダイレクトされたコマンドが成功すると、最終的にファイルを切り捨てる標準演算子のksh93固有のバリエーション<>;です。)<>

最初のバイトは削除されます(ファイルの残りの部分をそれ自体に書き、最後の部分を切り取ります)。

以下を使用して同じことを実行できますsh

{
  tail -c+2 < file &&
    perl -e 'truncate STDOUT, tell STDOUT'
} 1<> file

予定ですので参考にしてくださいまれではない希薄なファイル(後で再び穴を掘ることはできますがfallocate -d

読み取り/書き込みエラーが発生した場合は、tailファイルが部分的に上書きされたままになる可能性があります(たとえば、書き換え後に失敗するabcdefgh可能性があります)。エラーが発生したときに書き込みオフセットを報告してデータを回復する方法がわかるように、上記の内容を調整できます。まだあります:bcddefghbcdksh93

unset -v offset
{ tail -c+2 < file || false >#((offset=CUR)); } 1<>; file

$offset次に、正常に作成されたデータ量を含むifを設定します。

Linux(3.15以降)およびext4またはxfsファイルシステムでは、次のことができます。崩れるfallocate()システムコールまたはユーティリティのファイルシステムブロックサイズの倍数であるサイズとオフセットまたはバイトの範囲fallocate

例えば

fallocate -c -l 8192 file

ファイルの残りの部分を書き換えることなく、ファイルの最初の8192バイトが削除されます(FSブロックサイズが8192の除数であると仮定)。ただし、FSブロックサイズの倍数以外の部分を削除したい場合は役に立ちません。

答え2

「非常に大きなファイル」の意味によって異なります。あなたの限界は何ですか?

内容全体をメモリ(awk文字列として)に読み込み、部分文字列を元のファイルに書き戻すことができます。いくつかのステップでは、awkに生データと部分文字列の両方が含まれますが、0.5GBの場合、これは実行可能なソリューションです。 awkは私のラップトップで毎秒約80MBを処理できます。

Cでは、書き込み開始ポインタを移動するだけで簡単です。

答え3

Cでは同じinodeを使用し、作業ファイルを使用しないので、非常に簡単です。ただ、気をつけてよくしなければなりません。 2の累乗であれば十分ですが(64Kなど)、デバイスのブロックサイズ(4096など)を見つけるためにスペアクエリを実行する必要があります。

データフローを幼虫のように可視化します。つまり、データが新しい場所に移動できるように前方に伸びていくのです。

読み取り/書き込み用にファイルを開き、システム呼び出しの読み取り/書き込み内ですべての操作を実行して、FILE *ルーチンで発生する可能性があるバッファリングの問題を回避します。

ファイルの先頭(N)から削除するバイト数は、ブロック全体の数と一部のスペアバイト数です(これらのコンポーネントのいずれかまたは両方がゼロになる可能性があります)。

Aを探してX * 4096バイトを読みます。ここで、Xは(効率のために)大きいものとして選択されますが、愚かではありません。たぶん4MBのバッファが最適な選択かもしれません。

0 を探し、このバッファに必要な全ブロック数を記録します。私が紙から見たところによると、決して自分でラップされません。未読の次のバイトは前のブロックにはありません。

ファイルが不足するまですすぎ、繰り返します(検索の間に4 MBを追加)。短いブロックを正しく処理してください。

これにより、最後のNバイトの追加コピーが残り、システムコールでそれを切り捨てることができます。

パフォーマンスが良いはずです。書き込みはブロック整列されます。理論的には、各読み取りには冗長性のために2つのブロックアクセスが必要ですが、順次読み取りではこれを防ぎます(4MBは1024ブロックではなく1025ブロックを読み取ります)。

ddコマンドはスクリプトでこれを行うことができると思いますが、ddのブロックサイズオプションは検索と読み取りの両方に適用されるため、非常に非効率的です。

テスト方法:100 MBのランダムデータファイルをインポートしてcksumを計算します。次に、小さなNバイトファイルに追加します。コードcksumを実行し、ファイルが添付されたものと同じであることを確認します。時間を決めてください。 0、< 1ブロック、正確なブロック数、複数のブロック+ 1ビット、およびファイル全体を含むさまざまなN値でテストされました。

賞金を受け取るには、コードを書いてテストする必要がありますか?

答え4

あなたはprintfこれを行うことができますsedこれはとても危険です。一時コピーを使用せずに即座にファイルを編集するこの素晴らしい記事を読むことを強くお勧めします。する前に。

また、ファイルが大きすぎてメモリが不足すると失敗する可能性があることに注意してください。

printf '%s\n' "$(sed '1d' test.txt)" > test.txt

つまり、これはinodeを変更せずに機能し、ファイルを動的に変更する必要があります。

関連情報