elisp filenames path-manipulation

elisp - ¿Cuál es la forma correcta de unir múltiples componentes de ruta en una sola ruta completa en emacs lisp?



filenames path-manipulation (5)

Supongamos que tengo variables dir y file contienen cadenas que representan un directorio y un nombre de archivo, respectivamente. ¿Cuál es la forma correcta en emacs lisp para unirlos en una ruta completa al archivo?

Por ejemplo, si dir es "/usr/bin" y el file es "ls" , entonces quiero "/usr/bin/ls" . Pero si en cambio dir es "/usr/bin/" , sigo queriendo lo mismo, sin barra diagonal repetida.


Al leer el manual de Nombres de directorio , encontrará la respuesta:

Dado un nombre de directorio, puede combinarlo con un nombre de archivo relativo usando concat :

(concat dirname relfile)

Asegúrese de verificar que el nombre del archivo sea relativo antes de hacerlo. Si usa un nombre de archivo absoluto, los resultados podrían ser sintácticamente inválidos o referirse al archivo incorrecto.

Si desea utilizar un nombre de archivo de directorio para hacer tal combinación, primero debe convertirlo en un nombre de directorio usando el nombre de file-name-as-directory :

(concat (file-name-as-directory dirfile) relfile)

No intente concatenar una barra a mano, como en

;;; Wrong! (concat dirfile "/" relfile)

porque esto no es portatil Siempre use file-name-as-directory .

Otros comandos que son útiles son: file-name-directory file-name-nondirectory , file-name-directory file-name-nondirectory y otros en la sección Componentes del nombre del archivo .


Esto es lo que yo uso:

(defun catdir (root &rest dirs) (apply ''concat (mapcar (lambda (name) (file-name-as-directory name)) (push root dirs))))

Diferencias de @dbr''s:

  1. Devuelve un "nombre de directorio de emacs", es decir, un valor con una barra diagonal al final
  2. No expande la ruta si la root es relativa (ver notas)
  3. Trata a la root como la raíz, mientras que joindirs utilizará el primer componente que comienza con "/" como la raíz.

Notas

Muchas funciones de manejo de archivos (todas, la mayoría, ???) normalizarán barras redundantes y llamarán a expand-file-name (o similar) en las rutas relativas, por lo que # 2 y # 3 realmente no importan.


Puedes usar expand-file-name para esto:

(expand-file-name "ls" "/usr/bin") "/usr/bin/ls" (expand-file-name "ls" "/usr/bin/") "/usr/bin/ls"

Editar: esto solo funciona con nombres de directorio absolutos. Creo que la respuesta de Trey es la solución preferible.


Quería unir varios directorios anidados en una ruta. Originalmente utilicé múltiples llamadas de expand-file-name , como por ejemplo:

(expand-file-name "b" (expand-file-name "a" "/tmp")) "/tmp/a/b"

Sin embargo, esto es bastante detallado, y se lee al revés.

En su lugar, escribí una función que actúa como os.path.join de Python:

(defun joindirs (root &rest dirs) "Joins a series of directories together, like Python''s os.path.join, (dotemacs-joindirs /"/tmp/" /"a/" /"b/" /"c/") => /tmp/a/b/c" (if (not dirs) root (apply ''joindirs (expand-file-name (car dirs) root) (cdr dirs))))

Funciona así:

(joindirs "/tmp" "a" "b") "/tmp/a/b" (joindirs "~" ".emacs.d" "src") "/Users/dbr/.emacs.d/src" (joindirs "~" ".emacs.d" "~tmp") "/Users/dbr/.emacs.d/~tmp"


Si usa un archivo conveniente y la biblioteca de manipulación de directorios f.el , solo necesita f-join . El siguiente código es para aquellos que, por alguna razón, se niegan a usar esta biblioteca.

(defun os-path-join (a &rest ps) (let ((path a)) (while ps (let ((p (pop ps))) (cond ((string-prefix-p "/" p) (setq path p)) ((or (not path) (string-suffix-p "/" p)) (setq path (concat path p))) (t (setq path (concat path "/" p)))))) path))

Esto se comporta exactamente como os.path.join de Python.

ELISP> (os-path-join "~" "a" "b" "") "~/a/b/" ELISP> (os-path-join "~" "a" "/b" "c") "/b/c"

string-suffix-p no existe antes de Emacs 24.4 , así que escribí el mío en Check si una cadena termina con un sufijo en Emacs Lisp .