続・Perlで特定のディレクトリを列挙する
先日の日記で掲載した Perl のプログラムについて、真鍋さんが指摘してくれました。ありがたいことです。
コメントを返します。
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
#!/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");