Inplace-Edit #2
下のエントリの続き。
元記事で条件を端折ってしまったのですが、ファイル一覧はスクリプト内で生成されるので、INPLACE_EDIT(や、ワンライナー)は使用できないように思います。
File::Inplaceは初めて知りました。これで良さそうですが、できればコアモジュールで何とかしたいと思っています。
条件
※1番目の条件は、ファイル一覧抽出後にそれらを引数にしてexecするという手もありますね。と書いてみたものの、引数が多くなるとダメになってしまうので、この案は却下。
えーと、exec しちゃっていいなら xargs を組み合わせるのが定番かと思います(UNIX系OSの場合)。filelist.pl をファイル一覧を改行区切りで出力するスクリプト、replace.pl を inplace-edit で置換するスクリプトとして、以下の様な感じ。
% ./filelist.pl | xargs ./replace.pl
パスに空白を含む可能性を考慮するなら、./filelist.pl は "\000" を区切り文字にするようにして、以下のように。
% ./filelist.pl | xargs -0 -e ./replace.pl
あと、邪道な気もするけど、@ARGV を書き換えちゃうという手も。
たとえば、置換対象が「あるディレクトリ以下にあるテキストファイル全て」てな感じの場合こうする。
use strict; use warnings; use File::Spec; sub filelist { my ($path, $list) = @_; $list ||= []; opendir my $dir, $path or die; while (my $file = readdir $dir) { next if $file =~ /\A [.]+ \Z/x; my $file_path = File::Spec->catfile($path, $file); if ( -d $file_path ) { filelist($file_path, $list); } elsif ( -T $file_path ) { push @$list, $file_path; } } return $list; } sub main { my $path = $ARGV[0] || '.'; my $filelist = filelist($path); local $^I = ""; local @ARGV = @$filelist; while (<>) { s/foo/bar/; print; } } main;
別にファイルリストを一度に配列に入れる必要も無くて、置換部分は
while (my $file = next_file) { local @ARGV = ($file); while (<>) { s/foo/bar/; print; } }
な感じでも大丈夫。