Emacs Lisp: Walk Directory, List File Names

By Xah Lee. Date: . Last updated: .

Here's how to walk directory.

List Directory (No Subdir)

(directory-files DIRECTORY &optional FULL MATCH NOSORT)
Return a list of names of files in DIRECTORY. If FULL is non-nil, return absolute file names. If MATCH is non-nil, only return matched files. If NOSORT is non-nil, the list is not sorted, order is unpredictable.
;; walk dir, top level only

 (lambda (x) (insert x "\n"))
 (directory-files "~/em/emacs/i" nil "\.jpg$" t))

;; sample output

;; ztn_emacs_pinky_wedge_2018-02-24.jpg
;; xah_lee_photo_2018-06-28.jpg
;; xah-fly-keys_kinesis_2017-09_60363.jpg
;; the_book_of_shen_lang.jpg
;; sun_type_6_keyboard_meta_compose_altgraph_keys.jpg

[see Emacs Lisp: Regex Tutorial]

List Directory and All Subdir

(directory-files-recursively DIR REGEXP &optional INCLUDE-DIRECTORIES)
Return a list of all files under DIR and all its subdir that have file names matching REGEXP. (note, the match is done against file name only, not full path.) Returned list elements are full path. Optional argument INCLUDE-DIRECTORIES non-nil means also include in the output directories whose names match REGEXP.
;; walk dir and all subdir

 (lambda (x) (insert x "\n"))
 (directory-files-recursively "~/web/xahlee_info/emacs/emacs/" "\.png$"))

;; sample output

;; ~/web/xahlee_info/emacs/emacs/i/menu/emacs_goto_menu.png
;; ~/web/xahlee_info/emacs/emacs/i/menu/emacs_help_menu.png
;; ~/web/xahlee_info/emacs/emacs/i/menu/emacs_search_menus.png
;; ~/web/xahlee_info/emacs/emacs/i/menu/emcs_options_menu.png
;; ~/web/xahlee_info/emacs/emacs/i/menu/emcs_tools_menu.png
;; ~/web/xahlee_info/emacs/emacs/i/unicode/Carbon_emacs_22_unicode.png

Filter by Dir Depth Level

;; 2021-10-14 example of filter a dir by depth range

(require 'seq)

(setq $xrootDir "~/web/ergoemacs_org/")
(setq $xdirLevelMin 3)
(setq $xdirLevelMax 3)

(defun xah-find-count-slash (Path)
  "Count the number of slash in path.
Useful for finding the level of a nested dir.
Note: you should probably call `expand-file-name' on Path first to canonize path, to make sure dir name always ends in slash.
Version 2021-10-11"
  (seq-count (lambda (x) (char-equal x ?/)) Path))

(setq $xrootDirDepth (xah-find-count-slash (expand-file-name $xrootDir)))

(setq $result
         (lambda (x)
           (let (($df (- (xah-find-count-slash x) $xrootDirDepth)))
             (and (>= $df $xdirLevelMin) (<= $df $xdirLevelMax))))
         (directory-files-recursively $xrootDir ".")))

(mapc (lambda (x) (print x)) $result)

Skipping Subdir

In other programing languages (perl python golang), usually the dir walker lets you skip some specified directory. The walker calls a doFile function you write, and pass current dir or file, so you can skip it to prevent going into.

In elisp, there's a package find-lisp.el that lets you use a regex to filter dir, but is very slow. I do not recommend it. [Emacs Lisp: find-lisp.el]

To skip some subdir, it's faster if you just use directory-files-recursively then filter result.


 (lambda (path)
   (not (seq-some
         (lambda (x) (string-match x path))
         skipDirs )))
 (directory-files-recursively "/Users/xah/web/xahlee_info/" "\\.svg$" ))

seq-filter and seq-some are in emacs 25.

[see Emacs Lisp: Sequence Functions]

ErgoEmacs mascot-s276x226
Buy Xah Emacs Tutorial

Practical Elisp ⭐

Writing Command

Writing Script