それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

ディレクトリの中身を再帰的に表示する

ディレクトリの中身を再帰的に表示したい。linux だと find コマンドを使えば簡単です。しかしWindowsのFindコマンドがファイル内部を検索するものであり、Windowsでfind みたいなことを試みると、チョットめんどくさいのです。(WSHでも面倒)勉強のためなら再帰プログラムを再発明した方が良いけど、すぐ使いたいのでぱっぱと終らせた。

Rubyなら簡単にできました。

  • ruby を使ってディレクトリのファイルを再帰的に表示します。
  • find コマンドをオプション無し起動と同じです。

Linuxコマンドなら

$ find # sort の必要すらない.

Dir.glob を使う

#ピクチャディレクトリの中を見る
Dir.glob '/users/takuya/Pictures/**/*'
# => ['/users/takuya/Pictures/100MEDIA/IMAG0249.jpg', '/users/takuya/Pictures/100MEDIA/IMAG0252.jpg']
#カレントディレクトリ以下を再帰的に見る
Dir.chdir '/users/takuya/Pictures'
puts Dir.glob './**/*'
# => ['./100MEDIA/IMAG0249.jpg', './100MEDIA/IMAG0252.jpg']

glob のオプションに '**/*' を書くと、再帰的を意味するそうです。

**/ ワイルドカード */ の0回以上の繰り返しを意味し、 ディレクトリを再帰的にたどってマッチを行います。 例えば, foo/**/bar は foo/bar, foo/*/bar, foo/*/*/bar ... (以下無限に続く)に対してそれぞれ マッチ判定を行います。

http://www.ruby-lang.org/ja/man/html/Dir.html

Find モジュールを使う

require 'find'
inclue Find
find('/users/takuya/pictures/'){|e| puts e }
##結果
./100MEDIA/IMAG0249.jpg
./100MEDIA/IMAG0252.jpg

find の場合は必ずblock を取るので、block が無いと no block given になる。collection が戻ってこないのでチョット使いにくい

Dir.glob が使いやすい

Dir.glob を使うと、結果を配列で取得できます。だからソートが楽です。日付順ソートが簡単です。また『ある日付より前だけ』といったフィルタリングも簡ですね。

結果のソート例(Rubyマニュアルより)
Dir.glob("*").collect{|f| [File.mtime(f), f] }.
        sort{|a,b| b[0]<=>a[0] }.collect{|e| e[1] }

とすると、カレントディレクトリの"."、".."以外のファイルを更新時間の 新しい順にソートした配列を返します。

http://www.ruby-lang.org/ja/man/html/FAQ_C1C8A4DFB9FEA4DFA5E9A5A4A5D6A5E9A5EA.html

ディレクトリを再帰的に探して処理するより、ディレクトリを再帰的に走査してから処理をする方が速く楽なこともある。


2011-06-11追記 .htaccess が取得できない Dir::glob

.htaccess( dot files)に注意 、ドット始まりのファイルにはなぜかマッチしません

find なら.htaccess にもマッチするが
>> find('./event') {|e| puts e }
./event
./event/index.jsp
./event/form1.jsp
./event/.htaccess               #=>.htaccessがある
=> nil
Dir::globだと.htaccess にはマッチしない
>> Dir.glob("./event/**/*"){|e| puts e}
./event/form1.jsp
./event/index.jsp 
=> nil                         #=>.htaccessがない

dir::glob だと .gitとか .htaccess とか .bashrc とか見えないです.
これはピリオドにマッチしないのがfnmatch の基本動作だから正しい.

File::FNM_DOTMATCHで解決。

Dir::glob がピリオド(ドット)ファイルにマッチするようにするにはFile::FNM_DOTMATCHを使う。

.htaccess も取れる
>> Dir.glob("./event/**/*",File::FNM_DOTMATCH){|e| puts e}
./event/.
./event/..
./event/.htaccess
./event/form1.jsp
./event/index.jsp
=> nil

今度はウマく取れます.が ".." "." というディレクトリを示すファイルまで取れちゃいました.

"..", "."は不要

ドットにマッチして欲しいけど ディレクトリは要らない

>> Dir.glob("./event/**/*",File::FNM_DOTMATCH).reject{|e|
                      [".",".."].any?{|s| s== File::basename(e)  }
}
=> ["./event/.htaccess", "./event/form1.jsp", "./event/index.jsp"]

無理矢理だけれど、こんな感じ.