Perlで既存のファイルを書き換える時のBest Practicesは?
何か今日sed -iのようなことをしたくなったのですが、読み書き両用モード(+<)でopenしてseekしてtruncateして云々というのが面倒だったのでテンポラリファイル使ってごにょごにょしてしまいました。
すんなりできる方法を助言を頂きたく。。。
条件
※1番目の条件は、ファイル一覧抽出後にそれらを引数にしてexecするという手もありますね。と書いてみたものの、引数が多くなるとダメになってしまうので、この案は却下。
use File::Temp; use File::Copy; foreach my $file (qw(foo.txt)) { # open temporary file my $out = File::Temp->new; open my $in, q{<}, $file or die $!; while (<$in>) { s/foo/bar/; print $out $_; } close $in; close $out; # rename move($out->filename, $file); }
追記1
id:odzさんにスクリプト内でinplace-editするという方法を教えていただきました!感謝感謝。取り急ぎ報告をば。
my $filelist = ['foo.txt']; sub main { local $^I = ""; local @ARGV = @$filelist; while (<>) { s/foo/bar/; print; } } main;
としてみたところ、
などと言われてfoo.txtが
Can't do inplace edit on foo.txt: Permission denied at test.pl line 5.
夜も更けているため丸コピ実行しただけなので、明日ちゃんと調べてみようと思います。
xargsは環境によっては無い場合があるので、ちょっと|ι´Д`|っ < むりぽ
追記2
再びid:odzさんからトラックバックをいただきました。
>こっちでは空っぽというより、ファイルそのものが無くなりましたが。
あ、これは書き間違っていました。こちらでもファイルそのものが無くなりました。
my $filelist = ['foo.txt']; { local $^I = ".bak"; local @ARGV = @$filelist; while (<>) { s/foo/bar/; print; } unlink map { $_ . $^I } @$filelist; }
open,closeが無い分、すっきりしてますね。
ベンチマークでもINPLACE_EDITの方がかなり速いです。
Benchmark: timing 1000 iterations of File::Temp, INPLACE_EDIT... INPLACE_EDIT: 5 wallclock secs ( 0.75 usr + 2.49 sys = 3.24 CPU) @ 308.17/s (n=1000) File::Temp: 8 wallclock secs ( 2.88 usr + 5.11 sys = 7.99 CPU) @ 125.14/s (n=1000)
ということで、
@ARGVを局所化してINPLACE_EDITする(ただし可搬性を高めるためにバックアップファイルを作る)
ってのがBest Practiceっぽいですね。
id:odzさん、どうもありがとうございました。
そして、完全に他人のふんどしで相撲とったことに反省orz...