Copyright (C) 2000-2012 |
GNU Info (emacs-lisp-intro.info)Files ListMaking a List of Files ---------------------- The `recursive-lengths-list-many-files' function requires a list of files as its argument. For our test examples, we constructed such a list by hand; but the Emacs Lisp source directory is too large for us to do for that. Instead, we will write a function to do the job for us. In this function, we will use both a `while' loop and a recursive call. We did not have to write a function like this for older versions of GNU Emacs, since they placed all the `.el' files in one directory. Instead, we were able to use the `directory-files' function, which lists the names of files that match a specified pattern within a single directory. However, recent versions of Emacs place Emacs Lisp files in sub-directories of the top level `lisp' directory. This re-arrangement eases navigation. For example, all the mail related files are in a `lisp' sub-directory called `mail'. But at the same time, this arrangement forces us to create a file listing function that descends into the sub-directories. We can create this function, called `files-in-below-directory', using familiar functions such as `car', `nthcdr', and `substring' in conjunction with an existing function called `directory-files-and-attributes'. This latter function not only lists all the filenames in a directory, including the names of sub-directories, but also their attributes. To restate our goal: to create a function that will enable us to feed filenames to `recursive-lengths-list-many-files' as a list that looks like this (but with more elements): ("../lisp/macros.el" "../lisp/mail/rmail.el" "../lisp/makesum.el") The `directory-files-and-attributes' function returns a list of lists. Each of the lists within the main list consists of 13 elements. The first element is a string that contains the name of the file - which, in GNU/Linux, may be a `directory file', that is to say, a file with the special attributes of a directory. The second element of the list is `t' for a directory, a string for symbolic link (the string is the name linked to), or `nil'. For example, the first `.el' file in the `lisp/' directory is `abbrev.el'. Its name is `/usr/local/share/emacs/21.0.100/lisp/abbrev.el' and it is not a directory or a symbolic link. This is how `directory-files-and-attributes' lists that file and its attributes: ("/usr/local/share/emacs/21.0.100/lisp/abbrev.el" nil 1 1000 100 (15019 32380) (14883 48041) (15214 49336) 11583 "-rw-rw-r--" t 341385 776) On the other hand, `mail/' is a directory within the `lisp/' directory. The beginning of its listing looks like this: ("/usr/local/share/emacs/21.0.100/lisp/mail" t ... ) (Look at the documentation of `file-attributes' to learn about the different attributes. Bear in mind that the `file-attributes' function does not list the filename, so its first element is `directory-files-and-attributes''s second element.) We will want our new function, `files-in-below-directory', to list the `.el' files in the directory it is told to check, and in any directories below that directory. This gives us a hint on how to construct `files-in-below-directory': within a directory, the function should add `.el' filenames to a list; and if, within a directory, the function comes upon a sub-directory, it should go into that sub-directory and repeat its actions. However, we should note that every directory contains a name that refers to itself, called `.', ("dot") and a name that refers to its parent directory, called `..' ("double dot"). (In `/', the root directory, `..' refers to itself, since `/' has no parent.) Clearly, we do not want our `files-in-below-directory' function to enter those directories, since they always lead us, directly or indirectly, to the current directory. Consequently, our `files-in-below-directory' function must do several tasks: * Check to see whether it is looking at a filename that ends in `.el'; and if so, add its name to a list. * Check to see whether it is looking at a filename that is the name of a directory; and if so, - Check to see whether it is looking at `.' or `..'; and if so skip it. - Or else, go into that directory and repeat the process. Let's write a function definition to do these tasks. We will use a `while' loop to move from one filename to another within a directory, checking what needs to be done; and we will use a recursive call to repeat the actions on each sub-directory. The recursive pattern is `accumulate' (Note: Recursive Pattern.), using `append' as the combiner. Here is the function: (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/21.0.100/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, append 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 (or "." "..") (substring (car (car current-directory-list)) -1)) ;; then do nothing if filename is that of ;; current directory or parent () ;; 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)) The `files-in-below-directory' `directory-files' function takes one argument, the name of a directory. Thus, on my system, (length (files-in-below-directory "/usr/local/share/emacs/21.0.100/lisp/")) tells me that my version 21.0.100 Lisp sources directory contains 754 `.el' files. `files-in-below-directory' returns a list in reverse alphabetical order. An expression to sort the list in alphabetical order looks like this: (sort (files-in-below-directory "/usr/local/share/emacs/21.0.100/lisp/") 'string-lessp) automatically generated by info2www version 1.2.2.9 |