File::Find

id:fbisさんにアドバイスもらったので調べてみる。File::Find::Ruleっていうのは標準モジュールじゃないらしく手元のクイックリファレンスに載っていない。とりあえず後回しにしてFile::Findを試してみる。ActivePerlのリファレンスを読んでもよくわからん。クイックリファレンスでもわからないから英語だからって話じゃない。Findっていうくらいなので条件にあうファイルを集めてくるような動作をイメージしていた。んでグーグル先生の御神託でひねり出したのがこんなコード。

# sample of File::Find
use strict;
use File::Find;

our @wantedFiles;

find(\&wanted, $ARGV[0]);

for (@wantedFiles) {
    print "Delete $_\n" if /~$/;
}

sub wanted
{
    push(@wantedFiles, $File::Find::name) unless (/^\./);
}
__END__

配列がグローバルになるのが気に入らない。クイックリファレンスに載っているサンプルコードのサブルーチン名からしてwantedなのでどうしてもその中でファイル名をかき集めたくなる。グローバル変数をどうにかしたくてやってみたのが無名サブルーチンを使う方法。

# sample of File::Find
use strict;
use File::Find;

my @wantedFiles;

find(sub {push(@wantedFiles, $File::Find::name) unless (/^\./);}, $ARGV[0]);

for (@wantedFiles) {
    print "Delete $_\n" if /~$/;
}
__END__

OneLinerで収まるのならこれもいいのかも。だけど見方を変えてディレクトリとそのディレクトリ配下のファイルに対して操作する関数を渡しているって見方もできるなーと気づいて最終的に落ち着いたのはこんなコード

# sample of File::Find
use strict;
use File::Find;

find(\&wanted, $ARGV[0]);

sub wanted
{
    print "Delete $File::Find::name\n" if /~$/;
}
__END__

めでたしめでたし。なのだがさらに見方を変えるとこれはイテレータパターンの逆だよね。いや、逆って言うのも変かな。イテレータパターンの場合

Hoge hoge = new Hoge;
Iterator ite = new Iterator(hoge);
String str;
while (ite.hasNext()) {
    str = ite.Next();
    System.out.println("Delete " + str); // この部分を第一引数のサブルーチンでかく
}

うーんPerl恐るべし。

と思っていたらid:fbisさんからさらにコメントがついていた。

my @files = File::Find::Rule->file->in($dir);

これだけで$dirのディレクトリを再帰的に調べて、全てのファイルを戻り値として返してくれたりします。』

完璧です。配列に入ってくれれば後はどうにでもナル。お手軽にするならこれで十分だ。