Emacs lisp: `directory-files`
elisp interactive (2)
La función directory-files
devuelve el .
y ..
entradas también. Si bien en cierto sentido es cierto, solo así la función devuelve todas las entradas existentes, aún no he visto un uso para incluirlas. Por otro lado, cada vez que uso un directory-files
también escribo algo así como
(unless (string-match-p "^//.//.?$" ...
o para una mejor eficiencia
(unless (or (string= "." entry)
(string= ".." entry))
..)
Particularmente en el uso interactivo ( M-:
el código adicional es indeseable.
¿Hay alguna función predefinida que devuelve solo subentradas reales de un directorio de manera eficiente?
Puede hacer esto como parte de la llamada de función original.
(directory-files DIRECTORY &optional FULL MATCH NOSORT)
If MATCH is non-nil, mention only file names that match the regexp MATCH.
asi que:
(directory-files (expand-file-name "~/") nil "^//([^.]//|//.[^.]//|//.//..//)")
o:
(defun my-directory-files (directory &optional full nosort)
"Like `directory-files'' with MATCH hard-coded to exclude /"./" and /"../"."
(directory-files directory full "^//([^.]//|//.[^.]//|//.//..//)" nosort))
aunque algo más parecido a su propio enfoque podría hacer una envoltura más eficiente, realmente.
(defun my-directory-files (directory &optional full match nosort)
"Like `directory-files'', but excluding /"./" and /"../"."
(delete "." (delete ".." (directory-files directory full match nosort))))
aunque eso es procesar la lista dos veces, y sabemos que solo hay una instancia de cada uno de los nombres que deseamos excluir (y hay una buena probabilidad de que aparezcan primero), así que algo más como esto podría ser una buena solución si estás esperando tratar con grandes directorios de manera frecuente:
(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)))
Si usa f.el
, una conveniente biblioteca de manipulación de archivos y directorios, solo necesita f-entries
función f-entries
.
Sin embargo, si no desea utilizar esta biblioteca por algún motivo y está de acuerdo con una solución * nix no portátil, puede usar el comando 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)))
El código anterior es suficiente, pero para la explicación, lea más.
Deshágase de los puntos
De acuerdo con man ls
:
-A, --almost-all
do not list implied . and ..
Con split-string
que divide una cadena por espacios en blanco, podemos analizar la salida de ls
:
(split-string (shell-command-to-string "ls -A"))
Espacios en nombres de archivo
El problema es que algunos nombres de archivos pueden contener espacios. split-string
por defecto se divide por regex en variable split-string-default-separators
, que es "[ /f/t/n/r/v]+"
.
-1 list one file per line
-1
permite delimitar archivos por nueva línea, pasar "/n"
como separador único. Puede envolver esto en una función y usarlo con un directorio arbitrario.
(split-string (shell-command-to-string "ls -A1") "/n")
Recursión
Pero, ¿qué sucede si desea sumergirse de forma recursiva en subdirectorios y devolver los archivos para usarlos en el futuro? Si solo cambia el directorio y emite ls
, obtendrá nombres de archivos sin rutas, por lo que Emacs no sabría dónde se encuentran estos archivos. Una solución es hacer que ls
siempre devuelva rutas absolutas. De acuerdo con man ls
:
-d, --directory
list directory entries instead of contents, and do not dereference symbolic links
Si pasa la ruta absoluta al directorio con un comodín y la opción -d
, obtendrá una lista de rutas absolutas de archivos y subdirectorios inmediatos, de acuerdo con ¿Cómo puedo listar los archivos con su ruta absoluta en Linux? . Para obtener una explicación sobre la construcción de rutas, consulte En Elisp, ¿cómo se inserta correctamente la cadena de ruta con barras? .
(let ((path (file-name-as-directory (expand-file-name d))))
(split-srting (shell-command-to-string (concat "ls -A1d " path "*")) "/n"))
Omitir cadena nula
Los comandos de Unix tienen que agregar un espacio en blanco final a la salida, de modo que el aviso se encuentre en la nueva línea. De lo contrario, en lugar de:
user@host$ ls
somefile.txt
user@host$
habría:
user@host$ ls
somefile.txtuser@host$
Cuando pasa separadores personalizados a split-string
, trata esta línea nueva como una línea en sí misma. En general, esto permite analizar correctamente archivos CSV, donde una línea vacía puede ser datos válidos. Pero con ls
terminamos con una cadena nula, que debe omitirse al pasar t
como un tercer parámetro a split-string
.