指定したディレクトリを再帰的に検索してマッチしたファイルのリストを返す。

(defun search-file (path match &optional ignore)
  "Searches files of a directory recursively. Returns the list of matched files. "
  (let ((result nil)
        (regexp (if ignore
                    (concat "/[.]\\{1,2\\}$" "\\|" ignore)
                  "/[.]\\{1,2\\}$")))
    (cl-labels ((rec (path)
                     (dolist (f (directory-files path t))
                       (cond
                        ((string-match match f) (push f result))
                        ((and (file-accessible-directory-p f)
                              (not (string-match regexp f)))
                         (rec f))
                        (t nil)))))
      (rec path))
    (nreverse result)))

何もelispで実装しなくてもシェルコマンドで

(split-string
 (shell-command-to-string
  (format "find \"%s\" -name \"%s\"" (expand-file-name "~/LaTeX/") "*.pdf"))
  "\n")

こんな感じで書いても良いと思う。それでもelispで書いたほうが若干融通が効くので。

使用例

(search-file (expand-file-name "~/LaTeX") "\\.pdf$" "\\.git$")

引数match,ignore"\\.git$\\|\\.svn$"のように複数つなげても良い。この例では~/LaTeXのpdfファイルを.gitディレクトリを除外して検索している。

helm で使う

これを書いたのはあちこちに散らばっているpdfを絞り込み検索してviewerで見たいというのがあった。helmのソースに使いたいというのが動機です。

(defvar *pdf-search-path* '("~/LaTeX" "~/Downloads"))

(defvar helm-open-pdf-program
  (cl-case system-type
    ('darwin "open")
    ('gnu-linux "gnome-open")
    (otherwise nil)))

(defvar helm-source-pdf
  '((name . "Open PDF")
    (candidates . (lambda ()
                    (cl-loop for dir in *pdf-search-path*
                             nconc (search-file dir "\\.pdf$" "\\.git$"))))
    (action . helm-open-pdf)))

(defun helm-open-pdf (f)
  (start-process-shell-command
   "helm-pdf"
   nil
   (concat helm-open-pdf-program " " f)))

(defun helm-pdf ()
    (interactive)
    (helm :sources (list helm-source-pdf)))

M-x helm-pdfでpdfを絞り込みして開く。viewerは上のhelm-open-pdf-programに設定する。自分はMacとLinuxについてしか設定していない。

もう一例として旧ブログで書いたemacsでmp3を聞く(シンプルなmpg123-mode)のプレイリストの絞り込みにも使える。旧ブログでは自分のディレクトリ構造しか想定していなかったので気になっていたのです。たまにアクセスもあるのでGitHubのREADME.mdも修正しておきました。mpg123-simple - GitHub

(defvar helm-mpg123-playlists
  '((name . "Play List")
    (candidates . (lambda () (search-file *mpg123-dir* "\\.m3u$")))
    (action . helm-mpg123-open-playlist)))

(defun helm-mpg123-open-playlist (f) (interactive) (mpg123-mode f))

(defun helm-mpg123 () (interactive) (helm :sources (list helm-mpg123-playlists)))