Walking a Directory Tree

Finally, to round out this library, you can implement a function called walk-directory. Unlike the functions defined previously, this function doesn’t need to do much of anything to smooth over implementation differences; it just needs to use the functions you’ve already defined. However, it’s quite handy, and you’ll use it several times in subsequent chapters. It will take the name of a directory and a function and call the function on the pathnames of all the files under the directory, recursively. It will also take two keyword arguments: :directories and :test. When :directories is true, it will call the function on the pathnames of directories as well as regular files. The :test argument, if provided, specifies another function that’s invoked on each pathname before the main function is; the main function will be called only if the test function returns true.

  1. (defun walk-directory (dirname fn &key directories (test (constantly t)))
  2. (labels
  3. ((walk (name)
  4. (cond
  5. ((directory-pathname-p name)
  6. (when (and directories (funcall test name))
  7. (funcall fn name))
  8. (dolist (x (list-directory name)) (walk x)))
  9. ((funcall test name) (funcall fn name)))))
  10. (walk (pathname-as-directory dirname))))

Now you have a useful library of functions for dealing with pathnames. As I mentioned, these functions will come in handy in later chapters, particularly Chapters 23 and 27, where you’ll use walk-directory to crawl through directory trees containing spam messages and MP3 files. But before we get to that, though, I need to talk about object orientation, the topic of the next two chapters.


1One slightly annoying consequence of the way read-time conditionalization works is that there’s no easy way to write a fall-through case. For example, if you add support for another implementation to foo by adding another expression guarded with #+, you need to remember to also add the same feature to the or feature expression after the #- or the **ERROR** form will be evaluated after your new code runs.

2Another special value, :wild-inferiors, can appear as part of the directory component of a wild pathname, but you won’t need it in this chapter.

3Implementations are allowed to return :unspecific instead of **NIL** as the value of pathname components in certain situations such as when the component isn’t used by that implementation.

4This is slightly broken in the sense that if **PROBE-FILE** signals an error for some other reason, this code will interpret it incorrectly. Unfortunately, the CLISP documentation doesn’t specify what errors might be signaled by **PROBE-FILE** and probe-directory, and experimentation seems to show that they signal simple-file-errors in most erroneous situations.