よく、ディレクトリを再帰的になめてごちゃごちゃやりたいことがある。 簡単なものなら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でごにょごにょする。 書き捨てスクリプトではこっちの方が楽。