ファイルをコピーして文字を変更してリンクします。

ファイルをコピーして文字を変更してリンクします。

次のファイルがあります。

i36aasf5i7538i123
i47982i16537i1256
i1647i6458i3457
i1856i8456i43865

各行の最初のiがoに変わるファイルをコピーしたいです。次に、編集したファイルを元のファイルに再度リンクしようとします(可能であれば出力ファイルを指定せず)。

したがって、出力は次のようになります。

i36aasf5i7538i123
i47982i16537i1256
i1647i6458i3457
i1856i8456i43865
o36aasf5i7538i123
o47982i16537i1256
o1647i6458i3457
o1856i8456i43865

私はそうするいくつかの単一のライナーを知っています。しかし、sedを使用すると、エンコードに問題が発生しました(ファイルに珍しい文字が含まれています)。 Perlを使用すると、この問題はありませんが、できるだけ「エレガントに」Perlスクリプトに統合する方法を探しています。

私はUnixオペレーティングシステムを使用しています。

答え1

読み込んでいるファイルにデータを追加すると、以前に書き込んだデータを処理するため、無限ループに入ってファイルが永遠に大きくなる危険があります。

これによりこれを防ぐことができます。

perl -pe '
  BEGIN{seek(STDOUT,0,2);$end = tell STDOUT}
  last if tell(ARGV) > $end;
  s/i/o/' < file >> file

Perlスクリプトでは:

open OUT, ">>", "file" or die "open file: $!";
open IN, "<", "file" or die "open file: $!";
seek(OUT,0,2) or die "seek: $!";
$end = tell OUT;
while (tell IN < $end && <IN>) {
  s/i/o/;
  print OUT $_;
}
close IN;
close OUT;

答え2

sed 's/^i/o/;H;1h;$!d;x;q' <infile >>infile

ファイルがメモリに入るのに十分小さい場合、上記の方法が機能します。sed問題がない限り、エンコードの問題が発生する理由は考えられません。正常な人々はsedあなたが投げたいと思う利用できる文字エンコーディングを管理しなければなりません。

メモリに入るほど小さくない場合は、以下を理解するシステムで/dev/fd/[num]リンク(実際にはすべてのUnixシリーズシステム)、パイプの代わりにtmpファイルをドキュメントとして使用するシェルが提供されています。(ほとんどBourneシェルを含みますbashが、zshBSDやその他のバリエーションは除外さyashれるか、代わりにパイプを使用します)ashshdashbusybox sh${TMPDIR:-/tmp}、編集中のバッファを収容するのに十分な空き容量がある場合は、次のことが機能します。

sed -nf- file <<"" >>file
s/^i/o/
w /dev/fd/0
$r /dev/fd/0

これは、シェルが一時ファイルとドキュメントのファイル記述子をここから取得し、ここにスクリプトを作成するためにsed機能します。unlink()つまり、一時ファイル(したがって、ファイルシステムからの唯一のリンクの削除)その後、それを継承する子に分岐し、独自の sed状態を呼び出し前の状態に復元して、sed自己記述子を一時ファイルに入れます。この時点で、ファイルはsedstdin記述子としてのみ存在し、カーネルはファイルへのハンドルが存在する限りファイルを保持する必要がありますが、すべての記述子が解放されると、ファイルシステム0とのファイルリンクが削除されます。

したがって、sed削除された一時ファイルからスクリプトを読み込み、名前付きの-fライトwファイルに切り捨てます。これは、スクリプトを読み取る削除されたファイルへのリンクにすぎません。作成する各入力行を抽出する前に、次のコピーを入力してください。パターン空間。sed自動的に何でも印刷されます-nが、$最後の入力行ではr標準出力に書き込んだファイルが記録され、そのファイルは名前付き編集項目に追加されますw>>file

完了し、sedそのプロセスが終了すると、<<""here-docソースの最後の残りの記述子が閉じられ、カーネルはその後ファイルをクリーンアップします。同時に、他のプロセスは何らかの方法でファイルにアクセスできないため、sed他のプロセスが何らかの方法で作業バッファに影響を与える可能性の影響を受けません。

うまくいかない場合は、-nf-おそらくstdinとsed解釈していないからです。-(ほとんどはそうですが)を使用する必要があります-nf/dev/fd/0

答え3

ファイルの初期サイズに制限されたファイルに制限されたメモリマップを作成し、メモリマッピングを使用して「トリック」することができます。ファイルの別のハンドルを別々に開き、最後にそのハンドルを見つけます。メモリマップの繰り返しを開始して、読み取った各行をファイルの末尾にある別のファイルハンドルに書き込みます。代表pythonコード

import mmap
with open('file', 'r+') as f1, open('file', 'r+b')  as f2:
    mm = mmap.mmap(f2.fileno(), 0) #memory map restricted to current file length
    f1.seek(0, 2) #seek to end of file
    for line in mm:
            f1.write(line.replace('i', 'o', 1))

関連情報