如果你使用方便的文件和目录操作库
f.el
,只需要函数
f-entries
即可。
然而,如果由于某种原因你不想使用这个库,并且可以接受非便携式*nix解决方案,你可以使用
ls
命令。
(defun my-directory-files (d)
(let* ((path (file-name-as-directory (expand-file-name d)))
(command (concat "ls -A1d " path "*")))
(split-string (shell-command-to-string command) "\n" t)))
以下代码已经足够,但是如果需要解释,请继续阅读。
去除点
根据 man ls
:
-A, --almost-all
do not list implied . and ..
使用split-string
函数可以将字符串按空格分割,我们可以解析ls
命令的输出:
(split-string (shell-command-to-string "ls -A"))
文件名中的空格
问题在于有些文件名可能包含空格。默认情况下,split-string
函数会使用变量split-string-default-separators
中的正则表达式进行拆分,该变量的值为 "[ \f\t\n\r\v]+"
。
-1 list one file per line
-1
允许通过换行符来分隔文件,可以将"\n"
作为唯一的分隔符传递。您可以将其封装在一个函数中,并与任意目录一起使用。
(split-string (shell-command-to-string "ls -A1") "\n")
递归
但是如果您想递归地深入子目录,返回文件以备将来使用呢? 如果您只更改目录并发出 ls
命令,则会得到没有路径的文件名,因此Emacs不知道这些文件位于哪里。 一个解决方案是使 ls
始终返回绝对路径。 根据 man ls
:
-d, --directory
list directory entries instead of contents, and do not dereference symbolic links
如果您使用通配符并将绝对路径传递给目录,再使用-d
选项,则可以根据如何在Linux中列出文件的绝对路径?获取立即文件和子目录的绝对路径列表。有关路径构建的说明,请参见Elisp中如何正确插入斜杠以获取路径字符串?。
(let ((path (file-name-as-directory (expand-file-name d))))
(split-srting (shell-command-to-string (concat "ls -A1d " path "*")) "\n"))
省略空字符串
Unix命令需要在输出时添加尾随空格,以便提示符出现在新行上。否则会变成:
user@host$ ls
somefile.txt
user@host$
这里会有:
user@host$ ls
somefile.txtuser@host$
当您将自定义分隔符传递给
split-string
时,它会将这个换行符视为独立的一行。通常,这允许正确解析 CSV 文件,其中空行可能是有效数据。但是,在使用
ls
时,我们最终得到一个空字符串,应通过将
t
作为第三个参数传递给
split-string
来省略它。
(member entry '("." ".."))
是测试字符串是否等于一个固定集合中的元素的更好方法。 - Stefanmember
版本比(or (string= ...))
版本快约20%。在这种情况下,使用正则表达式比较会更慢。即使使用directory-files
的MATCH
参数,它也不会更快——因此,使用更通用的方法,在输出后使用(delete nil (mapcar ..
使用member
过滤,避免与MATCH
参数混淆,总体上更好。至少如果一个人决定使用自定义函数而不是直接使用directory-files
(我现在这样做)。 - kdb