よく、ディレクトリを再帰的になめてごちゃごちゃやりたいことがある。 簡単なものならfind使ってone linerで済ませるんだが、 ちょっとややこしい処理だとスクリプトにしたくなる。 そういう時に書いた使い捨てスクリプトを貯めておくページ。
まあ、処理自体は find . -name '*.scm' | xargs wc -l で済むことなんですが、 良く出てきそうなパターンなんで書いときます。 port-foldの中でごにょごにょすれば、コメントは飛ばすとか色々できますし。
#!/usr/bin/gosh (use gauche.parseopt) (use file.util) (use srfi-13) (define matcher #/\.scm$/) ;default (define (main args) (let* ((arg (parse-options (cdr args) (("c" () (set! matcher #/\.[ch]$/))))) (total (directory-fold (if (null? arg) "." (car arg)) (lambda (path knil) (if (matcher path) (let1 lines (with-input-from-file path (lambda () (port-fold (lambda (line count) (+ count 1)) 0 read-line))) (format #t "~6d ~a\n" lines path) (+ lines knil)) knil)) 0))) (format #t "~6d total\n" total)) 0)
directory版for-eachです。幅優先で木を処理します。 末端にだけ興味が有る場合はdirectory-foldを使うほうが良いでしょう。 fold系関数がピンと来ない人(私)にもお勧めです。--hira(2004/04/11 20:01:52 PDT)
#!/usr/bin/gosh (use file.util) ;directory-list (define (directory-for-each proc . base) (define (loop cwd) (define (down dir) (if (file-is-directory? dir) (loop dir))) (let1 ls (directory-list cwd :add-path? #t :children? #t) (for-each proc ls) (for-each down ls))) (for-each loop base)) (define (main args) (apply directory-for-each (lambda (path) (print "p:" path ) (print "d:" (sys-dirname path)) (print "b:" (sys-basename path)) (print "t:" (file-type path))) (cdr args)))
(2005/10/24 02:07:44 PDT) directory-for-eachを深さ優先にして、 unixのfind(1)やRubyのFind.findっぽくしたものです。 procが#fを返すとそのサブディレクトリを探査から外します(RubyのFind.pruneに相当)。
(use file.util) ; directory-list, file-type (define (make-selector . opts) (lambda (e) (and (file-exists? e) (eq? (apply file-type e opts) 'directory))))) (define (directory-traverse path proc . opts) (define direcotry? (apply make-selector opts)) (let rec ((path path) (depth 0)) (when (and (proc path depth) (direcotry? path)) (for-each (cute rec <> (+ depth 1)) (directory-list path :add-path? #t :children? #t)))))
使用例。カレントディレクトリ以下のファイルを段差付け表示。
(directory-traverse "." (lambda (path depth) (format #t "~a~a\n" (make-string depth) (sys-basename path)) #t))
以下はpost orderの処理もできるようにしたバージョン。 post orderの処理を行うthunkをprocの2番目の返り値にする。
(define (directory-traverse path proc . opts) (define directory? (apply make-selector opts)) (let rec ((path path) (depth 0)) (receive (continuep after) (proc path depth) (when (and (directory? path) continuep) (for-each (cute rec <> (+ depth 1)) (directory-list path :add-path? #t :children? #t))) (when after (after)))))
使用例。
(directory-traverse "." (lambda (path depth) (values #t (cut format #t "~a~a\n" (make-string depth) (sys-basename path)))))
一時リストを作ることを厭わなければ、
(glob #`",|dir|/**/*")
でdir以下の(ディレクトリ含む)全てのファイルのパス名のリストが返ってくるので あとはそれをmapやfilterでごにょごにょする。 書き捨てスクリプトではこっちの方が楽。