藻のブログ

日記,IT,学問(ジェンダー,人工知能など)について書かれることでしょう。

続・Perlで特定のディレクトリを列挙する

先日の日記で掲載した Perl のプログラムについて、真鍋さんが指摘してくれました。ありがたいことです。

takeda25.hatenablog.com

コメントを返します。

for (glob $dir."/*")

これは for (<$dir."/*">) のようにも書ける(知ってるか)。 あと、これはシェルのグロブだから、Linux とかだとドットファイルが無視されるみたい。

これは、(この記事を読んだあとで)『初めてのPerl』(第6版)13章で見ました。
ただ、記事の<$dir."/*">では動作しません。実際には<$dir/*>で正しく動作します〔<$dir."/*">を試してもうまく行かないので、だいぶ悩みました(笑)。〕。

< >で囲まれた部分は、" "で囲まれた部分と同じように解釈されるそうです(つまり、変数の展開も行なわれます。)。 筆者は" "内で変数の展開をさせるときは($fooでなく)${foo}と書くのがよいと考えているので、今回はfor (<${dir}/*>)と書くことにしました。

ところでglobは一般的な関数のようなものだと思っていたのですが、実際には演算子でした。より言語的に深い要素であるようです。

/^(?:\/.*)\/.+.git$/

(?:) の意味がないから外せるはず。

これは、そもそもキャプチャを目的としていないので,このグループでキャプチャを行なわないことを書いても意味がないということなのか、後半の\/.+\.git$がある以上,前半の(?:\/.*)という表現に意味がないということなのか、どちらなのかはわかりませんが、いずれにしてもその両方の点で意味がなさそうです。

あと、この正規表現だと a.git のようなファイルにも引っかかりそうだし、.git がディレクトリだという確認も必要では?

真鍋さんは、このプログラムが.gitというディレクトリを探すものだと思っていたそうです。(実際には、foo.gitのようなディレクトリを探すものです。)
foo.gitディレクトリであることの確認は必要なので、行なうことにしました。

それと、最初が / という縛りがあると相対ディレクトリに対して使えない(意図したもの?)。

今回は相対パスを対象にする必要がないということがわかっていたので、意図的です。(相対パスも意識するとなんとなく難しいので避けたということもありますが……。)(別に難しくもないのかも知れない。)

&repositories($_)

& は省略可能(知ってるかも)。

『初めてのPerl』で、「(言語側で)定義済みの関数と衝突する可能性があるので、はじめのうちは&をつけよう」というようなことが書かれていたので、つけました。まあ……、要らないとは思います。

@files

明示的に return を書くほうが一般的だと思う(あえてかな)。

『初めてのPerl』に

しかし、多くのPerlプログラマは、わざわざ7文字〔引用者注:returnのこと〕をタイプするのは、無駄な労力だと考えています。

と書かれているのですが……、実際にはそうでもないのかも知れません。

V2

  1. グロブの書き方を変更した。
  2. 正規表現を変更し、ディレクトリであることをチェックするようにした。
  3. 関数の呼び出しで&を省略した。
  4. retuenをつけた。
#!/usr/bin/perl -w

sub repositories {
    my ($dir) = @_;
    my @files;
    for (<${dir}/*>) {
        if (m%^/.+\.git$% && -d $_) {
            push @files, $_;
        } else {
            if (-d $_) {
                push @files, repositories($_);
            }
        }
    }
    return @files;
}

print "$_\n" for repositories("/opt/git");

www.oreilly.co.jp