次のファイルがあります。
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
が、zsh
BSDやその他のバリエーションは除外さyash
れるか、代わりにパイプを使用します)ash
sh
dash
busybox sh
${TMPDIR:-/tmp}
、編集中のバッファを収容するのに十分な空き容量がある場合は、次のことが機能します。
sed -nf- file <<"" >>file
s/^i/o/
w /dev/fd/0
$r /dev/fd/0
これは、シェルが一時ファイルとドキュメントのファイル記述子をここから取得し、ここにスクリプトを作成するためにsed
機能します。unlink()
つまり、一時ファイル(したがって、ファイルシステムからの唯一のリンクの削除)その後、それを継承する子に分岐し、独自の sed
状態を呼び出し前の状態に復元して、sed
自己記述子を一時ファイルに入れます。この時点で、ファイルはsed
stdin記述子としてのみ存在し、カーネルはファイルへのハンドルが存在する限りファイルを保持する必要がありますが、すべての記述子が解放されると、ファイルシステム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))