大きな (ギガバイト) サイズのファイルからいくつかの行を切り取る必要があるという問題が発生しました。メモリ内で読み取ろうとすると CPU を大量に消費する可能性があることを認識し、代わりにその場で編集したいと考えました...そこで、次の質問に至りました。
...さらにこれらもあります:
しかし、私は別のことについて考えていました。私は、( のような) あらゆるファイルシステムは、ext3
ディスクの領域にマップされるファイルのフラグメントのようなものを記述できるようにするために、リンク リストのようなものを採用する必要があると考えています (確信はありませんが)。
したがって、次のようなことが可能になるはずです。たとえば、bigfile.dat
次のようなファイルがあるとします (数字はバイト オフセットを示すはずですが、それらを揃えるのは少し難しいです)。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
L 1\n L 2\n L 3\n L 4\n L 5\n L 6\n
このファイルは、原理的には、ターミナル アプリケーションに読み込んで参照することができます。ツール を呼び出すと仮定しeditsegments bigfile.dat
、次のようにless -N bigfile.dat
同じファイルを表示するとします (行番号付き):
1 1 L 1
2 2 L 2 *
3 3 L 3
4 4 L 4 *
5 5 L 5
6 6 L 6
bigfile.dat (END)
たとえば、そこにコマンド (d
行の削除など) を入力し、別のキーまたはマウスをクリックして、上記の*
- で示されている場所をクリックすると、2 行目と 4 行目の間のすべてが削除されることを意味します。すると、プログラムは次のように応答します。
1 1 L 1
2 5 L 5
3 6 L 6
bigfile.dat (END)
ここで、左端の最初の列に「新しい」行番号 (切り取り後) が表示され、2 番目の列に「古い」行番号 (切り取り前) が表示され、その後に実際の行の内容が続くことがわかります。
さて、この疑似アプリケーションが終了した後に何が起こるか想像するとeditsegments
、まず第一に、はbigfile.dat
変更されません。ただし、同じディレクトリに、たとえば次の内容の追加のテキスト ファイルも存在することになりますbigfile.dat.segments
。
d 4:15 # line 2-4
...さらに、特別なファイル(いわゆる「シンボリックリンク」のようなもの)bigfile.dat.iedit
が表示されます。
さて、基本的に、このすべての結果として、 のbigfile.dat.iedit
ようなもので開こうとするとless -N bigfile.dat.iedit
、「編集された」コンテンツが取得されることになります。
1 L 1
2 L 5
3 L 6
bigfile.dat (END)
$FILE.iedit
... これは、が開かれるときに、まず$FILE.segments
を開いて読み取るようにオペレーティング システムに何らかの方法で指示することで実現できると思います。 はd 4:15
、元のファイルの 4 バイト目から 15 バイト目を省略するように指示します。その結果は次のようになります。
0 1 2 3 4 5 6 7 8 9 10 11 12,3,4 15 16 17 18 19 20 21 22 23
L 1\n
L
2
\n
L
3
\n
L
4
\n
L 5\n
L 6\n
0 1 2 3 ------------------------------->16 17 18 19 20 21 22 23
言い換えると -仮定ファイルのファイルシステムの概念では、コンテンツの各バイトには、チェーン内の次のバイトへの「リンク」も含まれています。つまり、スクリプトに基づいて新しいリンク リストを確立するようにファイルシステムに指示し、この変更されたリンク リストによって表されるコンテンツを特別なファイル (シンボリック リンクまたはパイプ) を通じて提供できるはずです。
タイトルの「スクリプト化」とは、つまり「新しい」リンク リストをスクリプト ファイル ( $FILE.segments
) で制御し、テキスト エディターでユーザーが編集できる (またはフロント エンド アプリケーションで生成できる) ことを意味します。「マルチパス」とは、bigfile.dat
このプロセスではまったく変更されないことを意味します。つまり、今日最初の (元の) ギガバイトを編集して、進行状況を ( $FILE.segments
) に保存し、明日 2 番目のギガバイトを編集して、再び進行状況を ( ) に保存する$FILE.segments
など、その間、元のファイルbigfile.dat
は変更されません。
すべての編集が完了したら、 のようなコマンド (たとえばeditsegments --finalize bigfile.dat
) を呼び出すことができます。このコマンドは、新しいリンク リストを の内容として永続的にエンコードするだけですbigfile.dat
(それに合わせて、bigfile.dat.segments
と を削除しますbigfile.dat.iedit
)。または、もっと簡単な方法として、次のようにすることもできます。
cp bigfile.dat.iedit /path/to/somewhere/else/bigfile.modified.dat
もちろん、elete スクリプト コマンドの他に、 eplace コマンドもd
使用できます。たとえば、次のようになります。r
r 16:18 AAA
... つまり、バイト 16 と 18 の間の内容を、スペースの後の次の 18-16+1=3 バイト (つまりAAA
) に置き換えます。リンク リストは、実際にスクリプト コマンドの内容自体に「フック」できます (以下のチャートには も含まれていますd
)。
0 1 2 3 4 5 6 7 8 9 10 11 12,3,4 15 16 17 18 19 20 21 22 23
L 1\n
L
2
\n
L
3
\n
L
4
\n
L
5
\n
L 6\n
0 1 2 3 ------------------------------->| | 19 20 21 22 23
.
.
.
.
.
.
.
\n
r
1
6
:
1
8
AAA
\n
.
.
.
.
さて、私はhexedit
(前述したように)のようなプログラムがここ) はファイルをその場で変更しますが、スクリプト作成の可能性 (ターミナルであっても GUI アプリケーションで制御できればさらに良い) の利点と、すべての編集が要求どおりであることを確認するまで元のファイルが実際に変更されないという利点が欲しいだけです。
このようなことが可能かどうかはわかりません。可能だとしても、専用のドライバー (ユーザー プログラムだけではなく) が必要になるかもしれません... しかし、とにかく尋ねる価値はあると思います。Linux にはこのようなものがあるのでしょうか?
回答をいただければ幸いです。よろしくお願い
します。
答え1
ディスク上のファイルの構造は、使用しているファイルシステムによって異なります。現実世界のファイルシステムでは、あなたが説明したようなリンクリストは使用されていません(そうするとfseek(3)
耐え難いものになります)。これに最も近いのは、Microsoftの脂肪基本的には、ポインタをデータ ブロックからシャドウイングする配列に移動します。
しかし、ほとんどのファイルシステムは、ファイル内のデータ ブロックへのポインタ ベースの参照を使用しているため、原理的には、いくつかのポインタ (ファイルの内容全体ではありません) をシャッフルし、ファイルの中央のブロックを空きとしてマークするだけで、ファイルのブロックを切り取ることができます。残念ながら、これはあまり便利な操作ではありません。ファイル ブロックはかなり大きく (通常 4KiB)、ファイル内の構造 (行またはその他のサブディビジョン) と適切に整列することはほとんどありません。
答え2
あなたが説明していることは、リプレイテキストエディタのやり直しリスト変更されていない元のファイルに対してやり直しリストgvim
属する。私はそれがそのようなものであると確信しています持続的な元に戻す/やり直しリストは、おそらく利用できるでしょうし、おそらくemacs
スクリプト経由で何でも好きなように実行できるようなリストが確実にあると私は知っていますelisp
。セッション間でEmacsの元に戻す履歴を保存する。
ちなみに、このような大きなファイルの場合は、不要なアクションをすべてオフにすることをお勧めします。例:自動保存、構文の強調表示(遅い大きいemacsファイルなど)があり、32ビットシステムのemacsには256MBのファイルサイズ制限。
確かに、あなたが提案したほど簡潔ではありませんが、変更点がそれほど多くない場合は使用可能かもしれません。
答え3
一般的に、ファイル全体をメモリに取り込まずにファイルを編集することはできません。実際にやりたいことは、特定の行を除いた古いファイルのコピーである新しいファイルを作成することだと想定しています。これは、UNIXユーティリティhead
とを使用して簡単に実現できますtail
。たとえば、ファイルから5、12、52行目以外のすべてをコピーするには、次のようにします。
head -n 4 bigfile.dat > tempfile.dat
tail -n +6 bigfile.dat | head -n 6 >> tempfile.dat
tail -n +13 bigfile.dat | head -n 39 >> tempfile.dat
tail -n 53 bigfile.dat >> tempfile.dat
これらのユーティリティについてよく知らない方のために、詳しく説明します。
このhead
ユーティリティは、ファイルの最初の n 行を出力します。位置引数が指定されていない場合は、ファイルとして標準入力を使用します。フラグは、-n
head に出力する行数を指定します。したがって、head -n 2
標準入力から最初の 2 行だけが出力されます。
このtail
ユーティリティは、ファイルの最後の n 行を出力します。head と同様に、ファイルまたは標準入力から読み取ることができます。-n フラグは、末尾から何行出力するかを tail に指示します。また、数字の前にプラス記号を付けて、ファイルの先頭からその行数だけ出力するように tail に指示することもできます。たとえば、tail -n 2
標準入力から最後の 2 行を出力します。ただし、tail -n +2
行番号 2 から始まるすべての行を出力します (行 1 は省略されます)。
一般的に、ファイルから範囲[x, y)の行を印刷したい場合は、次のようにします。
`tail -n +x | head -n d`
ここで、d = y - x です。これらのコマンドは新しいファイルを生成します。その後、必要に応じて古いファイルを削除できます。この方法の利点は、一度に 1 行だけをメモリに保持すればhead
よいtail
ため、RAM がすぐにいっぱいにならないことです。
答え4
sed スクリプトの仕事のようです。私の記憶が正しければ、そのようなタスクのために設計されたものです。行単位の処理、同じコマンド グループの繰り返し処理、正規表現がすべて 1 つのツールにまとめられています。このツールで作業できることはわかっていますが、詳細な情報を提供する以外に、これ以上のアドバイスはできません。マニュアルページ。