GNU Emacs 32.0.50 (9008bf64c3) のマニュアルを PLaMo 翻訳で機械翻訳したものです。誤訳を含むことがあります。原文: https://www.gnu.org/software/emacs/manual/html_node/eintr/Files-List.html ・ emacs-doc.conao3.com

14.9.2 ファイル一覧の作成

recursive-lengths-list-many-files 関数は、引数としてファイルのリストを必要とします。テスト用の例では、このようなリストを手作業で作成しましたが、Emacs Lisp のソースディレクトリは規模が大きすぎるため、それでは現実的ではありません。そこで、この処理を代行する関数を作成します。この関数では、while ループと再帰呼び出しの両方を使用します。

GNU Emacsの旧バージョンでは、すべての‘.el’ファイルが単一のディレクトリに配置されていたため、このような関数を記述する必要はありませんでした。その代わりに、単一のディレクトリ内で指定したパターンに一致するファイル名を一覧表示するdirectory-files関数を利用することができました。

しかし、最近のバージョンのEmacsでは、Emacs Lispファイルがトップレベルのlispディレクトリ配下のサブディレクトリに配置されています。この再編成により、ナビゲーションが容易になります。例えば、メール関連のファイルはすべて、mailというlispサブディレクトリに格納されています。一方で、この構成により、サブディレクトリを辿るファイル一覧表示関数を作成せざるを得なくなります。

files-in-below-directory というこの関数は、car、nthcdr、substring といった馴染みのある関数と、directory-files-and-attributes という既存の関数を組み合わせて作成できます。後者の関数は、サブディレクトリ名を含むディレクトリ内のすべてのファイル名を列挙するだけでなく、それらの属性も表示します。

改めて目的を述べると、recursive-lengths-list-many-files に次のような形式(ただし要素数はさらに多い)のファイル名リストを渡せる関数を作成することです:

("./lisp/macros.el"
 "./lisp/mail/rmail.el"
 "./lisp/hex-util.el")

directory-files-and-attributes 関数はリストのリストを返します。メインリスト内の各リストは13個の要素で構成されています。最初の要素はファイル名を含む文字列であり、GNU/Linux ではこれが ディレクトリファイル、すなわちディレクトリとしての特殊な属性を持つファイルである場合があります。リストの2番目の要素は、ディレクトリの場合は t、シンボリックリンクの場合は文字列(リンク先の名前)、または nil となります。

例えば、lisp/ディレクトリ内の最初の‘.el’ファイルはabbrev.elです。その名称は/usr/local/share/emacs/22.1.1/lisp/abbrev.elであり、ディレクトリやシンボリックリンクではありません。

directory-files-and-attributes がそのファイルとその属性を一覧表示する方法は以下の通りです。

("abbrev.el"
nil
1
1000
100
(1351051674579989697 . 1000000000)
(1173477761000000000 . 1000000000)
(1351050967734791805 . 1000000000)(17)
13188
"-rw-r--r--"
t
2971624
773)

一方、mail/ は lisp/ ディレクトリ内のディレクトリです。 その一覧の冒頭は次のようになっています。

("mail"
t
...
)

(各種属性について詳しく知りたい場合は、file-attributesのドキュメントを参照してください。なお、file-attributes関数はファイル名を列挙しないため、その第1要素はdirectory-files-and-attributesの第2要素となる点にご注意ください。)

新しい関数 files-in-below-directory には、指定されたディレクトリおよびその配下にあるすべてのディレクトリにおいて、「.el」ファイルを一覧表示させたいと考えています。

これにより、files-in-below-directoryをどのように構築すべきかのヒントが得られます。ディレクトリ内では、関数は‘.el’というファイル名をリストに追加すべきであり、また、ディレクトリ内でサブディレクトリが見つかった場合は、そのサブディレクトリに入り、同様の処理を繰り返すべきです。

ただし、すべてのディレクトリには、自身を指す .(「ドット」)と、親ディレクトリを指す ..(「ドットドット」)という名前が含まれている点に注意が必要です。(ルートディレクトリである / では、/ に親がないため、.. は自身を指します。)明らかに、files-in-below-directory 関数がこれらのディレクトリに入ることは避けたいところです。なぜなら、これらのディレクトリを辿ると、直接的あるいは間接的に常にカレントディレクトリに到達してしまうからです。

したがって、我々のfiles-in-below-directory関数はいくつかの処理を行う必要があります:

これらのタスクを実行するための関数定義を書いてみましょう。ディレクトリ内でファイル名を順に辿り、何を行うべきかを確認するにはwhileループを使用し、各サブディレクトリに対して処理を繰り返すには再帰呼び出しを使用します。再帰パターンはAccumulate(再帰パターン: accumulateを参照)で、結合子としてappendを使用します。

関数はこちらです:

(defun files-in-below-directory (directory)
  "List the .el files in DIRECTORY and in its sub-directories."
  ;; Although the function will be used non-interactively,
  ;; it will be easier to test if we make it interactive.
  ;; The directory will have a name such as
  ;;  "/usr/local/share/emacs/22.1.1/lisp/"
  (interactive "DDirectory name: ")
  (let (el-files-list
        (current-directory-list
         (directory-files-and-attributes directory t)))
    ;; while we are in the current directory
    (while current-directory-list
      (cond
       ;; check to see whether filename ends in '.el'
       ;; and if so, add its name to a list.
       ((equal ".el" (substring (car (car current-directory-list)) -3))
        (setq el-files-list
              (cons (car (car current-directory-list)) el-files-list)))
       ;; check whether filename is that of a directory
       ((eq t (car (cdr (car current-directory-list))))
        ;; decide whether to skip or recurse
        (if
            (equal "."
                   (substring (car (car current-directory-list)) -1))
            ;; then do nothing since filename is that of
            ;;   current directory or parent, "." or ".."
            ()
          ;; else descend into the directory and repeat the process
          (setq el-files-list
                (append
                 (files-in-below-directory
                  (car (car current-directory-list)))
                 el-files-list)))))
      ;; move to the next filename in the list; this also
      ;; shortens the list so the while loop eventually comes to an end
      (setq current-directory-list (cdr current-directory-list)))
    ;; return the filenames
    el-files-list))

files-in-below-directory 関数は、ディレクトリ名という1つの引数を取ります。

したがって、私のシステムでは、

(length
 (files-in-below-directory "/usr/local/share/emacs/22.1.1/lisp/"))

Lispソースディレクトリ内および配下に、1031個の「.el」ファイルがあることが示されています。

files-in-below-directory は、逆アルファベット順のリストを返します。リストをアルファベット順にソートする式は、次のようになります。

(sort
 (files-in-below-directory "/usr/local/share/emacs/22.1.1/lisp/")
 'string-lessp)

脚注

(17)

current-time-listがtの場合、3つのタイムスタンプはそれぞれ(20615 27034 579989 697000)、(17905 55681 0 0)、(20615 26327 734791 805000)となります。