【问题标题】:Emacs lisp: Concise way to get `directory-files` without "." and ".."?Emacs lisp:无需“.”即可获取`directory-files`的简洁方法和 ”..”?
【发布时间】:2013-06-14 10:12:00
【问题描述】:

函数directory-files 也返回... 条目。虽然从某种意义上说,只有这样函数才能返回所有现有条目,但我还没有看到包含这些条目的用途。另一方面,每次使用directory-files我也会写类似

(unless (string-match-p "^\\.\\.?$" ... 

为了提高效率

(unless (or (string= "." entry)
            (string= ".." entry))
   ..)

特别是在交互式使用中 (M-:),额外的代码是不可取的。

是否有一些预定义的函数可以有效地只返回目录的实际子条目?

【问题讨论】:

  • (member entry '("." "..")) 是测试字符串是否等于一组固定元素之一的更好方法。
  • 好点。我刚刚对其进行了基准测试,除了更易于阅读之外,member 版本比(or (string= ...)) 版本快约 20%。在这种情况下,使用正则表达式比较会更慢。即使使用directory-filesMATCH 参数,它也不会更快——因此使用member 之后使用(delete nil (mapcar .. 过滤输出的更通用方法可以避免混淆MATCH 参数,整体更好。至少如果一个人决定使用自定义函数而不是直接使用directory-files(我现在这样做)。

标签: file emacs directory elisp interactive


【解决方案1】:

只用remove-if怎么样?

(remove-if (lambda (x) (member x '("." "..")))
           (directory-files path))

【讨论】:

    【解决方案2】:

    如果你使用f.el,一个方便的文件和目录操作库,你只需要函数f-entries

    但是,如果您出于某种原因不想使用此库,并且可以使用非便携式 *nix 解决方案,则可以使用 ls command

    (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 选项将绝对路径传递到目录,那么您将根据How can I list files with their absolute path in linux? 获得直接文件和子目录的绝对路径列表。路径构造的解释见In Elisp, how to get path string with slash properly inserted?

    (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 来省略它。

    【讨论】:

    • 这不是最佳答案,最佳答案在下方(directory-files "/tmp" t directory-files-no-dot-files-regexp)
    【解决方案3】:

    您可以将其作为原始函数调用的一部分。

    (directory-files DIRECTORY &optional FULL MATCH NOSORT)
    
    If MATCH is non-nil, mention only file names that match the regexp MATCH.
    

    所以:

    (directory-files (expand-file-name "~/") nil "^\\([^.]\\|\\.[^.]\\|\\.\\..\\)")
    

    或:

    (defun my-directory-files (directory &optional full nosort)
      "Like `directory-files' with MATCH hard-coded to exclude \".\" and \"..\"."
      (directory-files directory full "^\\([^.]\\|\\.[^.]\\|\\.\\..\\)" nosort))
    

    虽然更类似于您自己的方法可能会产生更有效的包装器,真的。

    (defun my-directory-files (directory &optional full match nosort)
      "Like `directory-files', but excluding \".\" and \"..\"."
      (delete "." (delete ".." (directory-files directory full match nosort))))
    

    虽然这会处理列表两次,并且我们知道我们希望排除的每个名称只有一个实例(并且它们很有可能首先出现),所以如果您希望经常处理大型目录:

    (defun my-directory-files (directory &optional full match nosort)
      "Like `directory-files', but excluding \".\" and \"..\"."
      (let* ((files (cons nil (directory-files directory full match nosort)))
             (parent files)
             (current (cdr files))
             (exclude (list "." ".."))
             (file nil))
        (while (and current exclude)
          (setq file (car current))
          (if (not (member file exclude))
              (setq parent current)
            (setcdr parent (cdr current))
            (setq exclude (delete file exclude)))
          (setq current (cdr current)))
        (cdr files)))
    

    【讨论】:

    • 那么,没有简单的内置函数可用吗?
    • 你可以用(directory-files "~/" t directory-files-no-dot-files-regexp)得到同样的结果
    • @squiter 你的答案是最好的,但没有人看到它。
    猜你喜欢
    • 2014-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 2019-07-02
    • 1970-01-01
    • 2014-05-22
    相关资源
    最近更新 更多