file emacs directory elisp interactive

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 .